一个基于 .NET 开箱即用的爬虫库,使用复杂度极低。项目将爬虫分为 News (热榜) 和 Sectors (特定领域) 两大类。热榜预设支持微博、知乎、B站、百度、抖音、虎扑、头条、腾讯、掘金、澎湃、凤凰网、豆瓣、CSDN、博客园、IT之家、36氪等平台。特定领域提供动态数据集爬取 (Dynamic) 和彩票数据爬取 (Lottery) 等更灵活的爬虫功能。
$ dotnet add package Aneiang.Pa.CoreDynamic(动态数据集爬取)/ Lottery(彩票数据)⚠️ 建议抓取间隔 ≥ 5 分钟,避免频繁抓取导致 IP 被封禁。
⚠️ 爬取的数据仅限用于个人学习、研究或公益目的。不得用于商业售卖、攻击他人或任何非法活动,否则需自行承担法律责任。
- DEMO代码已经开源,支持DOCKER一键部署
- GitHub:https://github.com/AneiangSoft/Aneiang.Pa.News
- Gitee(同步):https://gitee.com/aneiangsoft/Aneiang.Pa.News
为了更好地组织和扩展功能,项目架构已进行调整:
src/News: 存放所有新闻热榜相关的爬虫项目。src/Sectors: 存放特定领域的爬虫项目,如动态爬虫和彩票爬虫。src/Core: 存放核心接口、模型和公共服务。项目提供两种聚合包和按需引用的单个功能包,开发者可根据需求选择。
Aneiang.Pa):包含所有已实现的功能。dotnet add package Aneiang.Pa
Aneiang.Pa.News):仅包含所有新闻热榜相关的爬虫。dotnet add package Aneiang.Pa.News
如果只需要特定平台或功能,可以按需引用单个包以减小依赖体积。
# 示例:仅引用百度热榜爬虫
dotnet add package Aneiang.Pa.BaiDu
| Package | 说明 |
|---|---|
| Aneiang.Pa | 聚合包,包含全部平台实现 |
| Aneiang.Pa.Core | 核心接口与模型、代理池功能 |
| Aneiang.Pa.AspNetCore | ASP.NET Core Web API 扩展(提供 RESTful API 控制器) |
| --- News (热榜) --- | --- |
| Aneiang.Pa.News | 热榜聚合包,包含以下所有新闻平台 |
| Aneiang.Pa.BaiDu | 百度热榜爬虫 |
| Aneiang.Pa.Bilibili | B 站热搜爬虫 |
| Aneiang.Pa.WeiBo | 微博热搜爬虫 |
| Aneiang.Pa.ZhiHu | 知乎热榜爬虫 |
| Aneiang.Pa.DouYin | 抖音热榜爬虫 |
| Aneiang.Pa.HuPu | 虎扑热帖/热榜爬虫 |
| Aneiang.Pa.TouTiao | 今日头条热榜爬虫 |
| Aneiang.Pa.Tencent | 腾讯热榜爬虫 |
| Aneiang.Pa.JueJin | 掘金热榜爬虫 |
| Aneiang.Pa.ThePaper | 澎湃热榜爬虫 |
| Aneiang.Pa.DouBan | 豆瓣热榜爬虫 |
| Aneiang.Pa.IFeng | 凤凰网热榜爬虫 |
| Aneiang.Pa.Csdn | CSDN热榜爬虫 |
| Aneiang.Pa.CnBlog | 博客园热榜爬虫 |
| --- Sectors (特定领域) --- | --- |
| Aneiang.Pa.Dynamic | 动态爬虫,可爬取任意网站的数据集合 |
| Aneiang.Pa.Lottery | 彩票数据爬虫 |
dotnet restore
dotnet build test/Aneiang.Pa.Demo/Aneiang.Pa.Demo.csproj
ScraperSource)dotnet run --project test/Aneiang.Pa.Demo
运行后,将在控制台看到抓取到的百度热榜数据。
Directory.Build.props 中的 Version 为准(当前为 2.1.5)。最简单的方式是使用全局注册方法,一键添加所有爬虫功能。
// 注册所有爬虫(推荐)
services.AddPaScraper();
如果你只需要特定功能,也可以按需注册:
// 仅注册热榜爬虫
services.AddNewsScraper();
// 仅注册彩票爬虫
services.AddLotteryScraper();
// 仅注册动态爬虫
services.AddDynamicScraper();
// 仅注册百度热榜爬虫
services.AddBaiDuScraper();
注册服务后,你可以从依赖注入容器中获取相应的服务实例。
获取热榜数据
// 通过工厂模式获取
var factory = scope.ServiceProvider.GetRequiredService<INewsScraperFactory>();
var scraper = factory.GetScraper(ScraperSource.BaiDu);
var result = await scraper.GetNewsAsync();
// 或直接注入单个爬虫
var baiduScraper = scope.ServiceProvider.GetRequiredService<IBaiDuNewScraper>();
var baiduResult = await baiduScraper.GetNewsAsync();
获取彩票数据
var lotteryScraper = scope.ServiceProvider.GetRequiredService<ILotteryScraper>();
var ssqResult = await lotteryScraper.GetLotteryDataAsync(LotteryType.SSQ); // 福利彩票
var dltResult = await lotteryScraper.GetLotteryDataAsync(LotteryType.DLT); // 体育彩票
支持配置多个代理服务器,自动轮询或随机选择代理进行请求,有效降低封禁风险。
http://user:password@host:port)在 appsettings.json 中配置:
{
"Scraper": {
"ProxyPool": {
"Enabled": true,
"Strategy": "RoundRobin",
"Proxies": [
"http://127.0.0.1:7890",
"http://user:password@proxy.example.com:8080",
"http://192.168.1.100:3128"
]
}
}
}
在代码中注册:
using Aneiang.Pa.Core.Proxy;
var builder = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
// 注册带代理池支持的默认 HttpClient
services.AddPaDefaultHttpClientWithProxy(
proxyConfiguration: context.Configuration.GetSection("Scraper:ProxyPool"));
// 注册爬虫服务(会自动使用配置的 HttpClient)
services.AddNewsScraper(context.Configuration);
})
.Build();
using Aneiang.Pa.Core.Proxy;
services.AddPaDefaultHttpClientWithProxy(
proxyConfigure: options =>
{
options.Enabled = true;
options.Strategy = ProxySelectionStrategy.RoundRobin; // 或 Random
options.Proxies = new List<string>
{
"http://127.0.0.1:7890",
"http://user:password@proxy.example.com:8080",
"http://192.168.1.100:3128"
};
});
services.AddNewsScraper();
提供开箱即用的 Web API 控制器,支持 RESTful API 调用和可选授权功能。
dotnet add package Aneiang.Pa.AspNetCore
设计目标:外部项目尽量“少写代码”。
AddPaScraperApi(...):注册 API + 缓存AddPaScraperAuthorization(...):按需启用授权(支持配置文件 + 可选代码覆盖)
using Aneiang.Pa.AspNetCore.Extensions;
using Aneiang.Pa.Lottery.Extensions;
using Aneiang.Pa.News.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
// 业务服务(热榜/彩票等)
builder.Services.AddNewsScraper(builder.Configuration);
builder.Services.AddLotteryScraper();
// 1) 注册 API + 缓存(读取 Scraper 配置节)
builder.Services.AddPaScraperApi(builder.Configuration);
// 2) 如需授权再启用(读取 Scraper:Authorization 配置节,支持可选代码覆盖)
builder.Services.AddPaScraperAuthorization(builder.Configuration);
var app = builder.Build();
app.MapControllers();
app.Run();
默认:
CacheProvider=None(不缓存),CacheDuration=01:00:00(1小时)。
{
"Scraper": {
"CacheProvider": "Memory",
"CacheDuration": "01:00:00",
"Redis": {
"Configuration": "localhost:6379,password=your_password,defaultDatabase=2",
"InstanceName": "Aneiang.Pa:"
}
}
}
CacheProvider 可选值:
None:不启用缓存Memory:进程内内存缓存Redis:Redis 分布式缓存Redis 连接字符串常用参数(StackExchange.Redis):
password=xxx:Redis 密码(requirepass / ACL)user=xxx:ACL 用户(Redis 6+)defaultDatabase=2:指定 DB(注意:Redis Cluster 不支持多 DB)授权支持三种策略:
ApiKey:请求头或查询参数 API KeyCustom:自定义策略 CustomAuthorizationFuncCombined:ApiKey 或 Custom 任意一种通过即可{
"Scraper": {
"Authorization": {
"Enabled": true,
"Scheme": "ApiKey",
"ApiKeys": ["demo-api-key-12345"],
"ApiKeyHeaderName": "X-API-Key",
"ApiKeyQueryParameterName": "apiKey",
"ExcludedRoutes": [
"/api/scraper/health",
"/api/scraper/news/sources"
],
"UnauthorizedMessage": "未授权访问"
}
}
}
代码只需:
builder.Services.AddPaScraperAuthorization(builder.Configuration);
builder.Services.AddPaScraperAuthorization(builder.Configuration, configure: opt =>
{
opt.Enabled = true;
opt.Scheme = Aneiang.Pa.AspNetCore.Options.AuthorizationScheme.Custom;
opt.CustomAuthorizationFunc = httpContext =>
{
var token = httpContext.Request.Headers["X-Demo-Token"].ToString();
return token == "valid-token" ? (true, null) : (false, null);
};
});
| 端点 | 方法 | 说明 | 示例 |
|---|---|---|---|
/api/scraper/news/{source} | GET | 获取指定平台的热榜 | /api/scraper/news/BaiDu |
/api/scraper/news/sources | GET | 获取所有支持的热榜源 | /api/scraper/news/sources |
/api/scraper/lottery/welfare/{type} | GET | 获取福利彩票开奖信息 | /api/scraper/lottery/welfare/SSQ |
/api/scraper/lottery/sport/{type} | GET | 获取体育彩票开奖信息 | /api/scraper/lottery/sport/DLT |
/api/scraper/lottery/types | GET | 获取所有支持的彩票类型 | /api/scraper/lottery/types |
/api/scraper/health | GET | 检查所有爬虫健康状态 | /api/scraper/health?timeoutMs=5000 |
/api/scraper/{source}/health | GET | 检查指定爬虫健康状态 | /api/scraper/BaiDu/health?timeoutMs=5000 |
为了避免 README 过长,这里将详细示例折叠。你可以直接展开查看完整示例。
dotnet add package Aneiang.Pa.Dynamic
services.AddDynamicScraper();
var scraperFactory = scope.ServiceProvider.GetRequiredService<IDynamicScraper>();
var testDataSets = await scraperFactory.DatasetScraper<CnBlogOriginalResult>("https://www.cnblogs.com/pick");
[HtmlContainer("div", htmlClass: "post-list", htmlId: "post_list", index: 1)]
[HtmlItem("article", htmlClass: "post-item")]
public class CnBlogOriginalResult
{
[HtmlValue("a", htmlClass: "post-item-title")]
public string Title { get; set; }
[HtmlValue(".", attribute: "data-post-id")]
public string Id { get; set; }
[HtmlValue("a", htmlClass: "post-item-title", attribute: "href")]
public string Url { get; set; }
[HtmlValue(htmlXPath: ".//a[@class=\"post-item-author\"]/span")]
public string AuthorName { get; set; }
[HtmlValue("a", htmlClass: "post-item-author", attribute: "href")]
public string AuthorUrl { get; set; }
[HtmlValue("p", htmlClass: "post-item-summary")]
public string Desc { get; set; }
[HtmlValue(htmlXPath: ".//footer[@class=\"post-item-foot\"]/span[1]")]
public string CreateTime { get; set; }
[HtmlValue(htmlXPath: ".//footer[@class=\"post-item-foot\"]/a[2]")]
public string CommentCount { get; set; }
[HtmlValue(htmlXPath: ".//footer[@class=\"post-item-foot\"]/a[3]")]
public string LikeCount { get; set; }
[HtmlValue(htmlXPath: ".//footer[@class=\"post-item-foot\"]/a[4]")]
public string ReadCount { get; set; }
}
<div id="post_list" class="post-list">
<article class="post-item" data-post-id="19326078">
<section class="post-item-body">
<div class="post-item-text">
<a class="post-item-title" href="https://www.cnblogs.com/ydswin/p/19326078" target="_blank">...</a>
<p class="post-item-summary">...</p>
</div>
</section>
</article>
</div>
HtmlContainerAttribute:数据集容器特性(支持 id/class/xpath)HtmlItemAttribute:数据项特性(支持 id/class/xpath)HtmlValueAttribute:字段取值特性(支持 id/class/xpath,可指定 attribute)PS:以上三个特性都支持 XPath 检索 HTML 标签,HTMLXPath 不为空时,其他属性不生效。
Aneiang.Pa 采用 MIT 许可证。