HttpClient适用于简单HTTP请求,需复用实例并设超时;JS渲染页面用PuppeteerSharp;解析HTML用HtmlAgilityPack;定时任务用IHostedService;高频采集需应对反爬。

用 HttpClient 抓取网页内容最常用也最可控
绝大多数简单 HTTP 请求场景下,HttpClient 是首选。它支持异步、可复用、能设超时和自定义头,比已过时的 WebClient 更可靠。
常见错误是每次请求都新建 HttpClient 实例,导致 socket 耗尽。正确做法是全局复用一个静态实例或通过 DI 注入:
private static readonly HttpClient _httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10)
};- 记得设置
Timeout,否则默认是无穷等待 - 若需携带 Cookie 或登录态,用
HttpClientHandler配置CookieContainer - 抓取含 JavaScript 渲染的页面时,
HttpClient只能拿到原始 HTML,无法执行 JS
处理 JavaScript 渲染页面得用 PuppeteerSharp
当目标页面依赖前端框架(如 Vue/React)动态加载数据,或需要模拟点击、滚动、输入等交互行为,纯 HTTP 请求就失效了。
PuppeteerSharp 是 C# 版 Puppeteer,底层驱动 Chromium,适合这类场景:
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
using var page = await browser.NewPageAsync();
await page.GoToAsync("https://example.com");
var content = await page.GetContentAsync();- 首次运行会自动下载 Chromium,耗时且体积大(约 150MB),建议预装或指定本地路径
- 内存占用高,不适合高频短时调用;长期运行需注意
browser和page的释放 - 不支持 .NET Framework,仅限 .NET Core 3.1+ / .NET 5+
解析 HTML 别手写正则,用 HtmlAgilityPack
拿到 HTML 字符串后,用正则提取标签内容极易出错——嵌套、转义、大小写、空格都会让表达式崩溃。
HtmlAgilityPack 提供 DOM 式遍历,稳定且语义清晰:
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(htmlString);
var titles = htmlDoc.DocumentNode.SelectNodes("//h2[@class='title']")
?.Select(n => n.InnerText.Trim())
.ToList();- XPath 语法支持有限,不支持
:nth-child等 CSS 伪类,复杂选择器得降级为多步遍历 - 对严重 malformed 的 HTML(如未闭合标签)容错强,但解析速度比原生 XML 解析器慢
- 若只提取 JSON 数据(如页面内
<script>window.__DATA__ = {...}</script>),直接用Regex匹配字符串反而更轻量
定时抓取别用 Thread.Sleep,改用 Timer 或后台服务
有人写控制台程序,外层套个 while(true) + Thread.Sleep(60000) 实现每分钟抓一次——这会阻塞线程、无法响应取消、异常后整个任务就停了。
- 短期调试可用
System.Threading.Timer,但需手动管理生命周期和异常捕获 - 生产环境推荐封装为
IHostedService,配合BackgroundService基类,天然支持依赖注入和优雅关闭 - 频率高于 1 次/秒时,注意目标网站反爬策略:加随机延时、轮换 User-Agent、限制并发请求数
真正难的不是“怎么取到数据”,而是怎么在变化的页面结构、封禁 IP、验证码、登录态过期之间维持稳定采集——这些没法靠某一个库解决,得组合策略、监控日志、及时人工介入。


















