前言
1.DotnetSpider概述
DotnetSpider 是一个轻量、灵活、高性能、跨平台的分布式网络爬虫框架,可以帮助 .NET 工程师快速的完成爬虫的开发。
2.DotnetSpider模块介绍
爬虫的基本流程是:下载数据(发送 HTTP 请求并获得返回的 resonse) -> 解析返回的文本(可以是 text、json、html) -> 存储解析到的数据,针对这三个主逻辑,我们可以再细下成以下模块。
- Scheduler 调度器:用于对采集请求的去重、采集顺序控制,默认实现了广度优先和深度优先两种调度器。调度器可以采用不同的 Hash 去重器,通常使用默认的 HashSetDuplicateRemover 即可,若是采集量很大可以使用 BloomFilterDuplicateRemover。若想要调度海量的请求或者有重启续跑这样的需求,则需要自行实现基于数据库(关系型数据库、Redis等)的调度器。
- 下载代理器:下载代理器可以部署在不同的机器上,若是单机爬虫则是每个爬虫实例会启动一个单独的下载代理器。下载代理器负责接收需要下载的请求并使用对应的下载器(HttpClient,Puppter 或者自定义实现的下载器)。
- 下载代理器注册服务:此服务仅用于接收下载代理器的注册、心跳,即便不启用起服务也并不会影响爬虫的使用。单机爬虫会默认启用一个内存型的注册服务。
- 统计服务:统计各个爬虫和下载代理器的运行状态,如爬虫总的请求数、成功的请求数等,下载代理器总的成功请求数、总的消耗时间等
- 请求供应接口:在很多场景下可能下载请求是可以提前知道或存在某个地方(可以是文件、数据库)
- 请求配置(Spider.ConfigureRequest):一般情况下请求都可以自动构建好,但在某些特别情况下如加 sign 等,可以统一处理。
- DataFlow: 数据流分两种,解析器和存储器。最极端情况是你不想搞那么复杂,解析和存储都自己在一个 DataFlow中实现。一个爬虫可以有多个 DataFlow,执行顺序按添加顺序,在任意一个 DataFlow 中抛出异常都会中断整个处理流程。
- 代理池:每个爬虫实例会启动一个代理后台服务,此后台服务定时从注册的 IProxySupplier中获取新的代理,每个获得的新代理需要经过检测成功才会入到代理池。在配置文件中或者 Builder创建时可以配置测试地址:ProxyTestUri
- 并发控制器:并发控制器以一定速度从 Scheduler 中获取请求并推到到消息队列中,这些请求会缓存在 RequestedQueue中,这个队列是使用低开销的 HashedWheelTimer 实现的,若在一定时间内未收到下载代理器返回的消息,则认为是 Timeout 触发重试直到超过重试次数限制。
DotnetSpider官网:github.com/dotnetcore/…
一、DotnetSpider爬虫框架
1.安装包
Install-Package DotnetSpider
Install-Package Serilog.AspNetCore
Install-Package Serilog.Sinks.Console
Install-Package Serilog.Sinks.File
Install-Package Serilog.Sinks.PeriodicBatching
2.创建 GithubSpider 类
using ConsoleTest;
using DotnetSpider.Scheduler.Component;
using Serilog.Events;
using Serilog;
using DotnetSpider;
using DotnetSpider.Scheduler;
using Microsoft.Extensions.Hosting;
//设置线程池
ThreadPool.SetMaxThreads(255, 255);
ThreadPool.SetMinThreads(255, 255);
//设置日志
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Console().WriteTo.File("logs/spider.log")
.CreateLogger();
var builder = Builder.CreateDefaultBuilder<GithubSpider>(options =>
{
// 每秒 1 个请求
options.Speed = 1;
});
builder.UseSerilog();
builder.UseQueueDistinctBfsScheduler<HashSetDuplicateRemover>();
await builder.Build().RunAsync();
Console.WriteLine("Bye!");
3.Program类
using ConsoleTest;
using DotnetSpider.Scheduler.Component;
using Serilog.Events;
using Serilog;
using DotnetSpider;
using DotnetSpider.Scheduler;
using Microsoft.Extensions.Hosting;
//设置线程池
ThreadPool.SetMaxThreads(255, 255);
ThreadPool.SetMinThreads(255, 255);
//设置日志
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Console().WriteTo.File("logs/spider.log")
.CreateLogger();
var builder = Builder.CreateDefaultBuilder<GithubSpider>(options =>
{
// 每秒 1 个请求
options.Speed = 1;
});
builder.UseSerilog();
builder.UseQueueDistinctBfsScheduler<HashSetDuplicateRemover>();
await builder.Build().RunAsync();
Console.WriteLine("Bye!");
4.运行
日志
[20:51:24 INF]
_____ _ _ _____ _ _
| __ \ | | | | / ____| (_) | |
| | | | ___ | |_ _ __ ___| |_| (___ _ __ _ __| | ___ _ __
| | | |/ _ \| __| '_ \ / _ \ __|\___ \| '_ \| |/ _` |/ _ \ '__|
| |__| | (_) | |_| | | | __/ |_ ____) | |_) | | (_| | __/ |
|_____/ \___/ \__|_| |_|\___|\__|_____/| .__/|_|\__,_|\___|_| version: 5.0.8.0
| |
|_|
[20:51:24 INF] RequestedQueueCount: 1000
[20:51:24 INF] Depth: 0
[20:51:24 INF] RetriedTimes: 3
[20:51:24 INF] EmptySleepTime: 60
[20:51:24 INF] Speed: 1
[20:51:24 INF] Batch: 4
[20:51:24 INF] RemoveOutboundLinks: False
[20:51:24 INF] StorageType: DotnetSpider.MySql.MySqlEntityStorage, DotnetSpider.MySql
[20:51:24 INF] RefreshProxy: 30
[20:51:24 INF] Agent is starting
[20:51:24 INF] Agent started
[20:51:24 INF] Initialize spider 602e62cc5f337be5627cd768, Github
[20:51:25 INF] 602e62cc5f337be5627cd768 DataFlows: Parser -> ConsoleStorage
[20:51:25 INF] 602e62cc5f337be5627cd768 register topic DotnetSpider_602e62cc5f337be5627cd768
[20:51:25 INF] Statistics service starting
[20:51:25 INF] Statistics service started
[20:51:30 INF] 602e62cc5f337be5627cd768 total 1, speed: 0, success 0, failure 0, left 1
[20:51:31 INF] 602e62cc5f337be5627cd768 download https://github.com/zlzforever, l7nvHQ== completed
DATA: {"username":"zlzforever","author":"Lewis Zou"}
[20:51:35 INF] 602e62cc5f337be5627cd768 total 1, speed: 0.10, success 1, failure 0, left 0
[20:51:40 INF] 602e62cc5f337be5627cd768 total 1, speed: 0.07, success 1, failure 0, left 0
[20:52:28 INF] Statistics service stopping
[20:52:28 INF] Statistics service stopped
[20:52:28 INF] 602e62cc5f337be5627cd768 stopped
[20:52:28 INF] Agent is stopping
[20:52:28 INF] Agent stopped
Bye!