using HtmlAgilityPack;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using HtmlAgilityPack.CssSelectors.NetCore;
using System.Text.RegularExpressions;
using System.Runtime.Serialization;
using System.Net.Http.Headers;
using System.Net;
using Newtonsoft.Json;
namespace NScrapy.Infra
{
public class HttpResponse : IResponse
{
private StringBuilder strBuilder = new StringBuilder();
private string reponsePlanText = string.Empty;
private string attr = string.Empty;
private HttpResponseMessage rawResponse;
private HtmlDocument doc = new HtmlDocument();
#region Manually these field beacuse HttpResponseHeader can not be Serialized by JsonConvert
private List acceptRanges;
private List via;
private List vary;
private List upgrade;
private bool? transferEncodingChunked;
private List transferEncoding;
private List trailer;
private List server;
private RetryConditionHeaderValue retryAfter;
private List proxyAuthenticate;
private List pragma;
private Uri location;
private EntityTagHeaderValue eTag;
private DateTimeOffset? date;
private bool? connectionClose;
private List connection;
private CacheControlHeaderValue cacheControl;
private TimeSpan? age;
private List warning;
private List wwwAuthenticate;
#endregion
#region Ignored Property While JsonFormat.SerializeObject
[JsonIgnore]
public Action Callback { get; set; }
[JsonIgnore]
public IRequest Request { get; set; }
[JsonIgnore]
public HttpResponseMessage RawResponseMessage
{
get
{
return rawResponse;
}
set
{
rawResponse = value;
AcceptRanges = RawResponseMessage.Headers.AcceptRanges.ToList();
Via = RawResponseMessage.Headers.Via.ToList();
Vary = RawResponseMessage.Headers.Vary.ToList();
Upgrade = RawResponseMessage.Headers.Upgrade.ToList();
TransferEncodingChunked = RawResponseMessage.Headers.TransferEncodingChunked;
TransferEncoding = RawResponseMessage.Headers.TransferEncoding.ToList();
Trailer = RawResponseMessage.Headers.Trailer.ToList();
Server = RawResponseMessage.Headers.Server.ToList();
RetryAfter = RawResponseMessage.Headers.RetryAfter;
ProxyAuthenticate = RawResponseMessage.Headers.ProxyAuthenticate.ToList();
Pragma = RawResponseMessage.Headers.Pragma.ToList();
Location = RawResponseMessage.Headers.Location;
ETag = RawResponseMessage.Headers.ETag;
Date = RawResponseMessage.Headers.Date;
ConnectionClose = RawResponseMessage.Headers.ConnectionClose;
Connection = RawResponseMessage.Headers.Connection.ToList();
CacheControl = RawResponseMessage.Headers.CacheControl;
Age = RawResponseMessage.Headers.Age;
Warning = RawResponseMessage.Headers.Warning.ToList();
WwwAuthenticate = RawResponseMessage.Headers.WwwAuthenticate.ToList();
}
}
#endregion
public string URL { get; set; }
public string ResponsePlanText
{
get
{
return this.reponsePlanText;
}
set
{
this.reponsePlanText = value;
doc.LoadHtml(value);
}
}
#region HttpResponseHeader Properties
//Manually create these field beacuse HttpResponseHeader can not be Serialized by JsonConvert
public List AcceptRanges { get => acceptRanges; set => acceptRanges = value; }
public List Via { get => via; set => via = value; }
public List Vary { get => vary; set => vary = value; }
public List Upgrade { get => upgrade; set => upgrade = value; }
public bool? TransferEncodingChunked { get => transferEncodingChunked; set => transferEncodingChunked = value; }
public List TransferEncoding { get => transferEncoding; set => transferEncoding = value; }
public List Trailer { get => trailer; set => trailer = value; }
public List Server { get => server; set => server = value; }
public RetryConditionHeaderValue RetryAfter { get => retryAfter; set => retryAfter = value; }
public List ProxyAuthenticate { get => proxyAuthenticate; set => proxyAuthenticate = value; }
public List Pragma { get => pragma; set => pragma = value; }
public Uri Location { get => location; set => location = value; }
public EntityTagHeaderValue ETag { get => eTag; set => eTag = value; }
public DateTimeOffset? Date { get => date; set => date = value; }
public bool? ConnectionClose { get => connectionClose; set => connectionClose = value; }
public List Connection { get => connection; set => connection = value; }
public CacheControlHeaderValue CacheControl { get => cacheControl; set => cacheControl = value; }
public TimeSpan? Age { get => age; set => age = value; }
public List Warning { get => warning; set => warning = value; }
public List WwwAuthenticate { get => wwwAuthenticate; set => wwwAuthenticate = value; }
#endregion
public HttpResponse()
{
}
///
///
///
/// selecter sample: .class div a::attr(href)
///
public IResponse CssSelector(string selector,Predicate filter=null)
{
var attrReg = new Regex(@"(?<=::attr\()[^()]*(?=\))");
var attrMatch = attrReg.Match(selector);
if(attrMatch.Success)
{
attr = attrMatch.Value;
//if matches .job-info h3 a::attr(href), remove ::attr(href)
selector = selector.Replace($"::attr({attr})","");
}
doc.LoadHtml(this.ResponsePlanText);
var elements=doc.QuerySelectorAll(selector);
return this.CreateFilteredResponse(elements, filter);
}
public IResponse CssSelector(IEnumerable possableSelector, Predicate filter=null)
{
IResponse returnValue = null;
foreach (var selector in possableSelector)
{
returnValue = this.CssSelector(selector,filter);
if(returnValue.ExtractFirst()!=null)
{
break;
}
}
return returnValue;
}
public IResponse XPathSelector(string xpath)
{
doc.LoadHtml(this.ResponsePlanText);
HtmlNodeCollection elements = null;
var attrReg = new Regex(@"(?<=[\w]@)[\w]*");
var attrMatch = attrReg.Match(xpath);
if (attrMatch.Success)
{
attr = attrMatch.Value;
xpath = xpath.Replace($"@{attr}", "");
}
elements = doc.DocumentNode.SelectNodes(xpath);
return this.CreateFilteredResponse(elements);
}
public IEnumerable Extract()
{
var attrValueRegPattern = string.Empty;
if(attr!=string.Empty)
{
attrValueRegPattern = $"(?<={attr}=\\s*['\"]+)[^'\"]*(?=['\"]+)";
}
foreach (var item in doc.DocumentNode.ChildNodes)
{
var value = string.Empty;
if (item.OuterHtml == string.Empty ||
item.OuterHtml == System.Environment.NewLine)
{
continue;
}
if (attr == string.Empty)
{
value = item.OuterHtml;
}
else if (attr == "text")
{
value = item.InnerText;
}
else
{
var attrValueMatch = new Regex(attrValueRegPattern);
value = attrValueMatch.Match(item.OuterHtml).Value;
}
yield return value;
}
}
public string ExtractFirst()
{
return this.Extract().FirstOrDefault();
}
public string ExtractLast()
{
return this.Extract().LastOrDefault();
}
private HttpResponse CreateFilteredResponse(IList elements,Predicate filter=null)
{
if (elements != null)
{
foreach (var node in elements)
{
var doExtract = true;
if(filter!=null)
{
var e = new ExtractArgs()
{
ExtractedValue = node.OuterHtml,
ExtractAgainstAttr = string.IsNullOrEmpty(attr) ? null : attr
};
doExtract = filter(e);
}
if (doExtract)
{
strBuilder.AppendLine(node.OuterHtml);
}
}
}
var returnValue = new HttpResponse()
{
ResponsePlanText = strBuilder.ToString(),
Request = this.Request,
URL = this.URL
};
returnValue.attr = this.attr;
strBuilder.Clear();
return returnValue;
}
}
}