C#网络爬虫开发
1前言
爬虫一般都是用Python来写,生态丰富,动态语言开发速度快,调试也很方便
但是
我要说但是,动态语言也有其局限性,笔者作为老爬虫带师,几乎各种语言都搞过,现在这个任务并不复杂,用我最喜欢的C#做小菜一碟~
2开始
之前做 OneCat 项目的时候,最开始的数据采集模块,就是用 C# 做的,同时还集成了 Chloe 作为 ORM,用 Nancy 做 HTTP 接口,结合 C# 强大的并发功能,做出来的效果不错。
这次是要爬一些壁纸,很简单的场景,于是沿用了之前 OneCat 项目的一些工具类,并且做了一些改进。
3HttpHelper
网络请求直接使用 .Net Core 标准库的 HttpClient,这个库要求使用单例,在 AspNetCore 里一般用依赖注入,不过这次简单的爬虫直接用 Console 程序就行。
把 HTML 爬下来后,还需要解析,在Python中一般用 BeautifulSoup,在C#里可以用 AngleSharp ,也很好用~
为了使用方便,我又封装了一个工具类,把 HttpClient 和 AngleSharp 集成在一起。
public static class HttpHelper {
public const string UserAgent =
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36";
public static HttpClientHandler Handler { get; }
public static HttpClient Client { get; }
static HttpHelper() {
Handler = new HttpClientHandler();
Client = new HttpClient(Handler);
Client.DefaultRequestHeaders.Add("User-Agent", UserAgent);
}
public static async Task<IHtmlDocument> GetHtmlDocument(string url) {
var html = await Client.GetStringAsync(url);
// todo 这个用法有内存泄漏问题,得优化一下
return new HtmlParser().ParseDocument(html);
}
public static async Task<IHtmlDocument> GetHtmlDocument(string url, string charset) {
var res = await Client.GetAsync(url);
var resBytes = await res.Content.ReadAsByteArrayAsync();
var resStr = Encoding.GetEncoding(charset).GetString(resBytes);
// todo 这个用法有内存泄漏问题,得优化一下
return new HtmlParser().ParseDocument(resStr);
}
}
这段代码里面有俩 todo ,这个内存泄漏的问题在简单的爬虫中影响不大,所以后面有大规模的需求再来优化吧~
4搞HTML
大部分爬虫是从网页上拿数据
如果网页是后端渲染出来的话,没有js动态加载数据,基本上用CSS选择器+正则表达式就可以拿到任何想要的数据。
经过前面的封装,请求网页+解析HTML只需要一行代码
IHtmlDocument data = await HttpHelper.GetHtmlDocument(url);
拿到 IHtmlDocument 对象之后,用 QuerySelector 传入css选择器,就可以拿到各种元素了。
例如这样,取出 <li> 元素下所有链接的地址
var data = await HttpHelper.GetHtmlDocument(url);
foreach (var item in data.QuerySelectorAll(".pagew li")) {
var link = item.QuerySelector("a");
var href = link?.GetAttribute("href");
if (href != null) await CrawlItem(href);
}
或者结合正则表达式
var data = await HttpHelper.GetHtmlDocument(url);
var page = data.QuerySelector(".pageinfo");
Console.WriteLine("拿到分页信息:{0}", page?.TextContent);
var match = Regex.Match(page?.TextContent ?? "", @"共\s(\d+)页(\d+)条");
var pageCount = int.Parse(match.Groups[1].Value);
for (int i = 1; i <= pageCount; i++) {
await CrawlPage(i);
}
正则表达式非常好用,爬虫必备~
这里再推荐一个好用的东西,菜鸟工具的在线正则表达式测试,拿到一个字符串之后,先在测试器里面写出一个能匹配的正则,再放到程序里,效率更高~
地址: https://c.runoob.com/front-end/854/
5JSON 处理
老生常谈的问题了
JSON 在 web 开发中很常见,无论是接口交互,还是本地保存数据,这都是一种很好的格式
.Net Core 自带的 System.Text.Json 还不错,不需要手动安装依赖,没有特殊需求的话,直接用这个就好了
这里的场景是要把采集的数据存到 JSON 里,即序列化,用以下的配置代码一把梭即可,可以应付大多数场景
var jsonOption = new JsonSerializerOptions {
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
写入文件
await File.WriteAllTextAsync("path", JsonSerializer.Serialize(data, jsonOption));
6下载文件
最简单就是直接用 HttpClient 获取 Response,然后 CopyToAsync 写到文件流里面
这个用法拿来下载几个小文件还可以,但多线程下载、断点重连、失败重试等方法就得自己实现了,比较繁琐。
所以这次我直接用了第三方库 Downloader,这个库看起来很猛,功能很多,我就不翻译了,详情见项目主页
项目地址: https://github.com/bezzad/Downloader
同样的,我把下载的功能也封装到 HttpHelper中
增加这部分代码
public static IDownloadService Downloader { get; }
public static DownloadConfiguration DownloadConf => new DownloadConfiguration {
BufferBlockSize = 10240, // 通常,主机最大支持8000字节,默认值为8000。
ChunkCount = 8, // 要下载的文件分片数量,默认值为1
// MaximumBytesPerSecond = 1024 * 50, // 下载速度限制,默认值为零或无限制
MaxTryAgainOnFailover = 5, // 失败的最大次数
ParallelDownload = true, // 下载文件是否为并行的。默认值为false
Timeout = 1000, // 每个 stream reader 的超时(毫秒),默认值是1000
RequestConfiguration = {
Accept = "*/*",
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
CookieContainer = new CookieContainer(), // Add your cookies
Headers = new WebHeaderCollection(), // Add your custom headers
KeepAlive = true,
ProtocolVersion = HttpVersion.Version11, // Default value is HTTP 1.1
UseDefaultCredentials = false,
UserAgent = UserAgent
}
};
static HttpHelper() {
// ...
Downloader = new DownloadService(DownloadConf);
}
使用方法依然是一行代码
await HttpHelper.Downloader.DownloadFileTaskAsync(url, filepath);
不过这次没有直接封装一个下载的方法,而是把 IDownloadService 对象做成属性,因为下载的时候往往要加一些“buff”
比如监听下载进度,看下面的代码
HttpHelper.Downloader.DownloadStarted += DownloadStarted;
HttpHelper.Downloader.DownloadFileCompleted += DownloadFileCompleted;
HttpHelper.Downloader.DownloadProgressChanged += DownloadProgressChanged;
HttpHelper.Downloader.ChunkDownloadProgressChanged += ChunkDownloadProgressChanged;
这个库提供了四个事件,分别是:
- 下载开始
- 下载完成
- 下载进度变化
- 分块下载进度变化
7进度条
有了这些事件,就可以实现下载进度条展示了,接下来介绍的进度条,也是 Downloader 这个库官方例子中使用的
项目地址: https://github.com/Mpdreamz/shellprogressbar
首先,把官网上的例子忘记吧,那几个例子实际作用不大。
Tick模式
这个进度条有两种模式,一种是它自己的 Tick 方法,先定义总任务数量,执行一次表示完成一个任务,比如这个:
using var bar = new ProgressBar(10, "正在下载所有图片", BarOptions);
上面代码定义了10个任务,每执行一次 bar.Tick() 就表示完成一次任务,执行10次后就整个完成~
IProgress<T> 模式
这个 IProgress<T> 是C#标准库的类型,用来处理进度条的。
ProgressBar 对象可以使用 AsProgress<T> 方法转换称 IProgress<T> 对象,然后调用 IProgress<T> 的 Report 方法,报告进度。
这个就很适合下载进度这种非线性的任务,每次更新时,完成的进度都不一样
Downloader的下载进度更新事件,用的是百分比,所以用这个 IProgress<T> 模式就很合适。
进度条嵌套
本爬虫项目是要采集壁纸,壁纸的形式是按图集组织的,一个图集下可能有多个图片
为了应对这种场景,可以用一个进度条显示总进度,表示当前正在下载某个图集
然后再嵌套子进度条,表示正在下载当前图集的第n张图片
然后的然后,再套娃一个孙子进度条,表示具体图片的下载进度(百分比)
这里用到的是 ProgressBar 的 Spawn 方法,会生成一个 ChildProgressBar 对象,此时更新子进度条对象的值就好了。
直接看代码吧
var list = // 加载图集列表
using var bar = new ProgressBar(list.Count, "正在下载所有图片", BarOptions);
foreach (var item in list) {
bar.Message = $"图集:{item.Name}";
bar.Tick();
foreach (var imgUrl in item.Images) {
using (var childBar = bar.Spawn(item.ImageCount,$"图片:{imgUrl}",ChildBarOptions)) {
childBar.Tick();
// 具体的下载代码
}
}
}
这样就实现了主进度条显示下载了第几个图集,子进度条显示下载到第几张图片。
然后具体下载代码中,使用 Downloader 的事件监听,再 Spawn 一个新的进度条显示单张图片的下载进度。
代码如下:
private async Task Download(IProgressBar bar, string url, string filepath) {
var percentageBar = bar.Spawn(100, $"正在下载:{Path.GetFileName(url)}", PercentageBarOptions);
HttpHelper.Downloader.DownloadStarted += DownloadStarted;
HttpHelper.Downloader.DownloadFileCompleted += DownloadFileCompleted;
HttpHelper.Downloader.DownloadProgressChanged += DownloadProgressChanged;
await HttpHelper.Downloader.DownloadFileTaskAsync(url, filepath);
void DownloadStarted(object? sender, DownloadStartedEventArgs e) {
Trace.WriteLine(
$"图片, FileName:{Path.GetFileName(e.FileName)}, TotalBytesToReceive:{e.TotalBytesToReceive}");
}
void DownloadFileCompleted(object? sender, AsyncCompletedEventArgs e) {
Trace.WriteLine($"下载完成, filepath:{filepath}");
percentageBar.Dispose();
}
void DownloadProgressChanged(object? sender, DownloadProgressChangedEventArgs e) {
percentageBar.AsProgress<double>().Report(e.ProgressPercentage);
}
}
注意所有的 ProgressBar 对象都需要用完释放,所以这里在 DownloadFileCompleted 事件里面 Dispose 了。
上面的是直接用 using 语句,自动释放。
进度条配置
这个东西的自定义功能还不错。
可以配置颜色、显示字符、显示位置啥的
var barOptions = new ProgressBarOptions {
ForegroundColor = ConsoleColor.Yellow,
BackgroundColor = ConsoleColor.DarkYellow,
ForegroundColorError = ConsoleColor.Red,
ForegroundColorDone = ConsoleColor.Green,
BackgroundCharacter = '\u2593',
ProgressBarOnBottom = true,
EnableTaskBarProgress = RuntimeInformation.IsOSPlatform(OSPlatform.Windows),
DisplayTimeInRealTime = false,
ShowEstimatedDuration = false
};
EnableTaskBarProgress 这个选项可以同时更新Windows任务状态栏上的进度
具体配置选项可以直接看源码,里面注释很详细。
如果 Spawn 出来的子进度条没配置选项,那就会继承上一级的配置。
8小结
用 C# 来做爬虫还是舒服的,至少比 Java 好很多
做控制台应用,打包成exe也方便分发

C#网络爬虫开发的更多相关文章
- 崔庆才Python3网络爬虫开发实战电子版书籍分享
资料下载地址: 链接:https://pan.baidu.com/s/1WV-_XHZvYIedsC1GJ1hOtw 提取码:4o94 <崔庆才Python3网络爬虫开发实战>高清中文版P ...
- 《Python3网络爬虫开发实战》PDF+源代码+《精通Python爬虫框架Scrapy》中英文PDF源代码
下载:https://pan.baidu.com/s/1oejHek3Vmu0ZYvp4w9ZLsw <Python 3网络爬虫开发实战>中文PDF+源代码 下载:https://pan. ...
- Python 3网络爬虫开发实战》中文PDF+源代码+书籍软件包
Python 3网络爬虫开发实战>中文PDF+源代码+书籍软件包 下载:正在上传请稍后... 本书书籍软件包为本人原创,在这个时间就是金钱的时代,有些软件下起来是很麻烦的,真的可以为你们节省很多 ...
- Python 3网络爬虫开发实战中文 书籍软件包(原创)
Python 3网络爬虫开发实战中文 书籍软件包(原创) 本书书籍软件包为本人原创,想学爬虫的朋友你们的福利来了.软件包包含了该书籍所需的所有软件. 因为软件导致这个文件比较大,所以百度网盘没有加速的 ...
- Python 3网络爬虫开发实战中文PDF+源代码+书籍软件包(免费赠送)+崔庆才
Python 3网络爬虫开发实战中文PDF+源代码+书籍软件包+崔庆才 下载: 链接:https://pan.baidu.com/s/1H-VrvrT7wE9-CW2Dy2p0qA 提取码:35go ...
- 《Python 3网络爬虫开发实战中文》超清PDF+源代码+书籍软件包
<Python 3网络爬虫开发实战中文>PDF+源代码+书籍软件包 下载: 链接:https://pan.baidu.com/s/18yqCr7i9x_vTazuMPzL23Q 提取码:i ...
- 《Python3 网络爬虫开发实战》开发环境配置过程中踩过的坑
<Python3 网络爬虫开发实战>学习资料:https://www.cnblogs.com/waiwai14/p/11698175.html 如何从墙内下载Android Studio: ...
- 《Python3 网络爬虫开发实战》学习资料
<Python3 网络爬虫开发实战> 学习资料 百度网盘:https://pan.baidu.com/s/1PisddjC9e60TXlCFMgVjrQ
- Python 3网络爬虫开发实战书籍
Python 3网络爬虫开发实战书籍,教你学会如何用Python 3开发爬虫 本书介绍了如何利用Python 3开发网络爬虫,书中首先介绍了环境配置和基础知识,然后讨论了urllib.reques ...
- Python3网络爬虫开发实战PDF高清完整版免费下载|百度云盘
百度云盘:Python3网络爬虫开发实战高清完整版免费下载 提取码:d03u 内容简介 本书介绍了如何利用Python 3开发网络爬虫,书中首先介绍了环境配置和基础知识,然后讨论了urllib.req ...
随机推荐
- day15-Servlet04
Servlet04 12.ServletConfig 12.1ServletConfig基本介绍 ServletConfig类是为Servlet程序配置信息的类 Servlet对象和ServletCo ...
- SQLSever事务
1. 为什么要使用事务? 当一个存储过程或多个SQL语句(指代insert.update.delete类型)依次执行时候, 如果其中一条或几条发生错误,但是其他的还会继续执行,会造成数据的不一致,非常 ...
- 2022-11-03 Acwing每日一题
本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的.同时也希望 ...
- 报错:com.mysql.jdbc.MysqlDataTruncation: Data truncation xxxx
报错 Out of range value for column 'pk' at row 1:表的字段长度不够 Data too long for column 'ip' at row 1:表的字段长 ...
- Mybatis下的SQL注入漏洞原理及防护方法
目录 一.前言 二.SQL 注入漏洞原理 1.概述 2.漏洞复现 3.修复建议 三.Mybatis 框架简介 1.参数符号的两种方式 2.漏洞复现 四.Mybatis 框架下的 SQL 注入问题及防护 ...
- 重学c#系列——委托和匿名函数[二十五]
前言 简单介绍一下什么是委托. 正文 以前也写过委托,这次算是重新归档,和新的补充吧. https://www.cnblogs.com/aoximin/p/13940125.html 有些人说委托是函 ...
- HCIE Routing&Switching之MPLS基础理论
技术背景 90年代初期,互联网流量快速增长,而由于当时硬件技术的限制,路由器采用最长匹配算法逐跳转发数据包,成为网络数据转发的瓶颈:于是快速路由技术成为当时研究的一个热点:在各种方案中,IETF确定了 ...
- Asp.Net Core&Jaeger实现链路追踪
前言 随着应用愈发复杂,请求的链路也愈发复杂,微服务化下,更是使得不同的服务分布在不同的机器,地域,语言也不尽相同.因此需要借助工具帮助分析,跟踪,定位请求中出现的若干问题,以此来保障服务治理,链路追 ...
- 【Java SE】Day03流程控制语句
一.流程控制(顺序结构) 二.选择结构 1.多分支中case的穿透性 2.switch的括号可以是 基本/引用类型(String.enum枚举) 三.循环结构 for循环结束后内存消失,效率高 四.扩 ...
- 【每日一题】【链表or双指针循环条件】2022年2月26日-NC96 判断一个链表是否为回文结构
描述给定一个链表,请判断该链表是否为回文结构.回文是指该字符串正序逆序完全一致. 思路: public boolean isPail (ListNode head) { ListNode node = ...