c#实现爬虫程序
作者:暗断肠 发布时间:2023-04-19 18:59:14
图1
如图1,我们工作过程中,无论平台网站还是企业官网,总少不了新闻展示。如某天产品经理跟我们说,推广人员想要抓取百度新闻中热点要闻版块提高站点百度排名。要抓取百度的热点要闻版本,首先我们先要了解站点https://news.baidu.com/请求头(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;
}
还有一个我自定义传参对象,当然无论传入或者传出的对象都是你们根据自己实际业务需求定义的:
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; }
}
根据展示的代码,我们可以发现HttpWebRequest对象里面都封装了很多Request headers报文参数,我们可以根据该网站的Request headers信息在微软提供的HttpWebRequest对象里设置(看代码报文参数注释,都有写相关参数说明,如果理解错误,望告之,谢谢),然后发送请求获取Response data解析数据。
还有补充一点,爬虫程序能够使用 * 最好使用 * ,这样降低被封杀机率,提高抓取效率。但是 * 也分质量等级,对于某一些HTTPS站点,可能对应需要质量等级更加好的 * 才能穿透,这里暂不跑题,后续我会写一篇关于 * 质量等级文章详说我的见解。
C#代码如何使用 * ?
微软NET框架也为了我们提供一个使用 * 的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));
// 如果只是设置 * 加端口,例如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;
}
关于 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);
}
}
下面主要展示抓取结果。
图4
如图4,抓取效果,一个简单爬虫程序就这样子完成了。
来源:https://www.cnblogs.com/wzk153/p/9145684.html


猜你喜欢
- AnyChat(全名叫Anychat SDK),也叫音视频互动开发平台;是一套跨平台的即时通讯解决方案,基于先进的H.264视频编码标准、A
- Settings -> Editor -> General -> Use soft wraps in editor&nbs
- Json格式是常见的读写形式。读写Json文件也是常用的操作。这次来实践一下Json文件的读写。首先在SD卡上的读写权限是一定要申请的。那么
- 在使用SpringSecurity中,大伙都知道默认的登录数据是通过key/value的形式来传递的,默认情况下不支持JSON格式的登录数据
- 本文实例为大家分享了SpringBoot Http远程调用的具体代码,供大家参考,具体内容如下一、在实现远程调用时可以使用feign与htt
- 实践过程效果代码public partial class Form1 : Form{ public Form1()
- 本文主要为大家分析了图书商城的用户模块,具体内容如下1、用户模块的相关类创建domain:Userdao:UserDaoservice:Us
- Java程序默认输出为Console,如果要想将Console输出结果保存到文件中,则需要做如下配置:在JAVA程序上右键--> Ru
- 实践过程😜富文本芝麻粒儿提醒:标签是成对出现的就不要省略,有的不是成对的在修改了后就恢复过来,如下方alpha示例,否则多了很容易出现意外的
- 1、使用HttpWebRequest/HttpWebResonse和WebClientHttpWebRequest request = (H
- LinkedList与ArrayList都是List接口的具体实现类。LinkedList与ArrayList在功能上也是大体一致,但是因为
- 前章知识: 点此跳转HTML简介:超文本是一种组织信息的方式,它通过超级链接方法将文本中的文字、图表与其他信息媒体相关联。这些相互关联的信息
- 推荐教程:idea2021以下版本适用,通过无限重置试用持续激活:https://www.jb51.net/article/198343.h
- 一、Intent的用途Intent主要有以下几种重要用途: 1. 启动Activity:可以将Intent对象传递给startActivit
- 一、项目运行环境配置:Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Eclispe
- 思路不清晰的小伙伴可以先在es中把聚合代码写出来{ "aggs": { "
- 题目:求1+2!+3!+...+20!的和程序分析:此程序只是把累加变成了累乘。程序设计:public class Ex21 {  
- using System; using System.Drawing; using System.Collec
- 本文实例为大家分享了Java工具类DateUtils的具体代码,供大家参考,具体内容如下import java.text.ParseExce
- 本文实例讲述了C#中派生类调用基类构造函数用法。分享给大家供大家参考。具体分析如下:这里的默认构造函数是指在没有编写构造函数的情况下系统默认