爬⾍浅谈⼀:⼀个简单c#爬⾍程序
这篇⽂章只是简单展⽰⼀个基于HTTP请求如何抓取数据的⽂章,如觉得简单的朋友,后续我们再慢慢深⼊研究探讨。
图1:
如图1,我们⼯作过程中,⽆论平台⽹站还是企业官⽹,总少不了新闻展⽰。如某天产品经理跟我们说,推⼴⼈员想要抓取百度新闻中热点要闻版块提⾼站点百度排名。要抓取百度的热点要闻版本,⾸先我们先要了解站点news.baidu/请求头(Request headers)信息。
为什么要了解请求头(Request headers)信息?
原因是我们可以根据请求头信息某部分报⽂信息伪装这是⼀个正常HTTP请求⽽不是⼈为爬⾍程序躲过站点封杀,⽽成功获取响应数据(Response data)。
如何查看百度新闻⽹址请求头信息?
图2:
如图2,我们可以打开⾕歌浏览器或者其他浏览器开发⼯具(按F12)查看该站点请求头报⽂信息。从图中可以了解到该百度新闻站点可以接受text/html等数据类型;语⾔是中⽂;浏览器版本
是Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36等等报⽂信息,在我们发起⼀个HTTP请求的时候直接携带该报⽂信息过去。当然并不是每个报⽂信息参数都必须携带过去,携带⼀部分能够请求成功即可。
那什么是响应数据(Response data)?
图3:
如图3,响应数据(Response data)是可以从⾕歌浏览器或者其他浏览器中开发⼯具(按F12)查看到的,响应可以是json数据,可以是DOM树数据,⽅便我们后续解析数据。
当然您可以学习任意⼀门开发语⾔开发爬⾍程序:C#、NodeJs、Python、Java、C++。
但这⾥主要讲述是C#开发爬⾍程序。微软为我们提供两个关于HTTP请求HttpWebRequest,HttpWebResponse对象,⽅便我们发送请求获取数据。以下展⽰下C# HTTP请求代码:
private string RequestAction(RequestOptions options)
{
string result = string.Empty;
IWebProxy proxy = GetProxy();
var request = (HttpWebRequest)WebRequest.Create(options.Uri);
request.Accept = options.Accept;
//在使⽤curl做POST的时候, 当要POST的数据⼤于1024字节的时候, curl并不会直接就发起POST请求, ⽽是会分为俩步,
//发送⼀个请求, 包含⼀个Expect: 100 -continue, 询问Server使⽤愿意接受数据
//接收到Server返回的100 - continue应答以后, 才把数据POST给Server
//并不是所有的Server都会正确应答100 -continue, ⽐如lighttpd, 就会返回417 “Expectation Failed”, 则会造成逻辑出错.
request.ServicePoint.Expect100Continue = false;
request.ServicePoint.UseNagleAlgorithm = false;//禁⽌Nagle算法加快载⼊速度
if (!string.IsNullOrEmpty(options.XHRParams)) { request.AllowWriteStreamBuffering = true; } else { request.AllowWriteStreamBuffering = false; }; //禁⽌缓冲加快载⼊速度
request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");//定义gzip压缩页⾯⽀持
request.ContentType = options.ContentType;//定义⽂档类型及编码
request.AllowAutoRedirect = options.AllowAutoRedirect;//禁⽌⾃动跳转
request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36";//设置User-Agent,伪装成Google Chrome浏览器            request.Timeout = options.Timeout;//定义请求超时时间为5秒
request.KeepAlive = options.KeepAlive;//启⽤长连接
if (!string.IsNullOrEmpty(options.Referer)) request.Referer = options.Referer;//返回上⼀级历史链接
request.Method = options.Method;//定义请求⽅式为GET
if (proxy != null) request.Proxy = proxy;//设置代理服务器IP,伪装请求地址
if (!string.IsNullOrEmpty(options.RequestCookies)) request.Headers[HttpRequestHeader.Cookie] = options.RequestCookies;
request.ServicePoint.ConnectionLimit = options.ConnectionLimit;//定义最⼤连接数
if (options.WebHeader != null && options.WebHeader.Count > 0) request.Headers.Add(options.WebHeader);//添加头部信息
if (!string.IsNullOrEmpty(options.XHRParams))//如果是POST请求,加⼊POST数据
{
byte[] buffer = Encoding.UTF8.GetBytes(options.XHRParams);
if (buffer != null)
{
request.ContentLength = buffer.Length;
request.GetRequestStream().Write(buffer, 0, buffer.Length);
}
}
using (var response = (HttpWebResponse)request.GetResponse())
{
/
///获取请求响应
//foreach (Cookie cookie in response.Cookies)
//    options.CookiesContainer.Add(cookie);//将Cookie加⼊容器,保存登录状态
if (response.ContentEncoding.ToLower().Contains("gzip"))//解压
{
using (GZipStream stream = new GZipStream(response.GetResponseStream(), CompressionMode.Decompress))
{
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
result = reader.ReadToEnd();
}
}
}
else if (response.ContentEncoding.ToLower().Contains("deflate"))//解压
{
using (DeflateStream stream = new DeflateStream(response.GetResponseStream(), CompressionMode.Decompress))
{
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
result = reader.ReadToEnd();
}
}
}
else
{
using (Stream stream = response.GetResponseStream())//原始
{
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
result = reader.ReadToEnd();
}
}
}
}
request.Abort();
return result;
}
View Code
还有⼀个我⾃定义传参对象,当然⽆论传⼊或者传出的对象都是你们根据⾃⼰实际业务需求定义的:
public class RequestOptions
{
///<summary>
///请求⽅式,GET或POST
/
//</summary>
public string Method { get; set; }
///<summary>
/// URL
///</summary>
public Uri Uri { get; set; }
///<summary>
///上⼀级历史记录链接
///</summary>
public string Referer { get; set; }
///<summary>
/
//超时时间(毫秒)
///</summary>
public int Timeout = 15000;
///<summary>
///启⽤长连接
///</summary>
public bool KeepAlive = true;
///<summary>
///禁⽌⾃动跳转
///</summary>
public bool AllowAutoRedirect = false;
/
//<summary>
///定义最⼤连接数
///</summary>
public int ConnectionLimit = int.MaxValue;
///<summary>
///请求次数
///</summary>
public int RequestNum = 3;
///<summary>
///可通过⽂件上传提交的⽂件类型
///</summary>
public string Accept = "*/*";
///<summary>
///内容类型
///</summary>
public string ContentType = "application/x-www-form-urlencoded";
///<summary>
///实例化头部信息
///</summary>
private WebHeaderCollection header = new WebHeaderCollection();
///<summary>
///头部信息
/
//</summary>
public WebHeaderCollection WebHeader
{
get { return header; }
set { header = value; }
}
///<summary>
///定义请求Cookie字符串
///</summary>
public string RequestCookies { get; set; }
///<summary>
/
//异步参数数据
///</summary>
public string XHRParams { get; set; }
}
View Code
根据展⽰的代码,我们可以发现HttpWebRequest对象⾥⾯都封装了很多Request headers报⽂参数,我们可以根据该⽹站的Request headers信息在微软提供的HttpWebRequest对象⾥设置(看代码报⽂参数注释,都有写相关参数说明,如果理解错误,望告之,谢谢),然后发送请求获取Response data解析数据。
还有补充⼀点,爬⾍程序能够使⽤代理IP最好使⽤代理IP,这样降低被封杀机率,提⾼抓取效率。但是代理IP也分质量等级,对于某⼀些HTTPS站点,可能对应需要质量等级更加好的代理IP才能穿透,这⾥暂不跑题,后续我会写⼀篇关于代理IP质量等级⽂章详说我的见解。
C#代码如何使⽤代理IP?
微软NET框架也为了我们提供⼀个使⽤代理IP 的System.Net.WebProxy对象,关于使⽤代码如下:
private System.Net.WebProxy GetProxy()
{
System.Net.WebProxy webProxy = null;
try
{
// 代理链接地址加端⼝
string proxyHost = "192.168.1.1";
string proxyPort = "9030";
// 代理⾝份验证的帐号跟密码
/
/string proxyUser = "xxx";
//string proxyPass = "xxx";
// 设置代理服务器
webProxy = new System.Net.WebProxy();
// 设置代理地址加端⼝
webProxy.Address = new Uri(string.Format("{0}:{1}", proxyHost, proxyPort));
// 如果只是设置代理IP加端⼝,例如192.168.1.1:80,这⾥直接注释该段代码,则不需要设置提交给代理服务器进⾏⾝份验证的帐号跟密码。
//webProxy.Credentials = new System.Net.NetworkCredential(proxyUser, proxyPass);
}
catch (Exception ex)
{
Console.WriteLine("获取代理信息异常", DateTime.Now.ToString(), ex.Message);
}
return webProxy;
}
View Code
关于 System.Net.WebProxy对象参数说明,我在代码⾥⾯也做了解释。
如果获取到Response data数据是json,xml等格式数据,这类型解析数据⽅法我们这⾥就不详细说了,请⾃⾏百度。这⾥主要讲的是DOM树 HTML数据解析,对于这类型数据有⼈会⽤正则表达式来解析,也有⼈⽤组件。当然只要能获取到⾃⼰想要数据,怎么解析都是可以。这⾥主要讲我经常⽤到解析组件 HtmlAgilityPack,引⽤DLL为(using HtmlAgilityPack)。解析代码如下:
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(simpleCrawlResult.Contents);
HtmlNodeCollection liNodes = htmlDoc.DocumentNode.SelectSingleNode("//div[@id='pane-news']").SelectSingleNode("div[1]/ul[1]").SelectNodes("li");
if (liNodes != null && liNodes.Count > 0)
{
for (int i = 0; i < liNodes.Count; i++)
{
string title = liNodes[i].SelectSingleNode("strong[1]/a[1]").InnerText.Trim();
string href = liNodes[i].SelectSingleNode("strong[1]/a[1]").GetAttributeValue("href", "").Trim();
Console.WriteLine("新闻标题:" + title + ",链接:" + href);
}
}
View Code
另外附上HtmlAgilityPack学习链接 wwwblogs/asxinyu/p/CSharp_HtmlAgilityPack_XPath_Weather_Data.html
下⾯主要展⽰抓取结果。
图4:
python爬虫开发
如图4,抓取效果,⼀个简单爬⾍程序就这样⼦完成了。。。(这⾥只是⼩弟不才个⼈见解,如有错误,望各位⼤⽜多多指教)