使用 HttpClient 发起 GET 请求获取网页内容是 C# 最轻量现代做法,需复用实例、设超时、检查状态码、用 ReadAsStringAsync 读取 HTML;再用 HtmlAgilityPack 解析,注意 XPath 属性加 @、优先 InnerHtml + HtmlDecode;基础反反爬需设 User-Agent 和延时。

用 HttpClient 发起 GET 请求获取网页内容
直接用 HttpClient 是 C# 最轻量、最现代的做法,比已过时的 WebClient 更可靠,也比 HttpWebRequest 更简洁。注意必须复用同一个 HttpClient 实例,否则容易触发端口耗尽(SocketException: Too many open files)。
- 在类级别声明并复用:
private static readonly HttpClient client = new HttpClient(); - 设置超时避免卡死:
client.Timeout = TimeSpan.FromSeconds(10); - 记得处理响应状态码:
if (!response.IsSuccessStatusCode),不要只靠异常判断 - 用
await response.Content.ReadAsStringAsync()读取 HTML 字符串,不是ReadAsByteArrayAsync(除非你要解析二进制)
用 HtmlAgilityPack 解析 HTML 提取链接和文本
HtmlAgilityPack 是 .NET 生态里最稳定、兼容性最好的 HTML 解析库,能容忍 malformed HTML(比如缺少闭合标签),比正则匹配靠谱得多。它不依赖浏览器引擎,纯内存解析,速度快。
- 安装包:
dotnet add package HtmlAgilityPack - 加载 HTML:
var doc = new HtmlDocument(); doc.LoadHtml(htmlString); - 查所有链接:
doc.DocumentNode.SelectNodes("//a[@href]"),注意 XPath 中属性要带@ - 提取文本内容别用
InnerText(含换行/空白多),优先用InnerHtml+WebUtility.HtmlDecode()清理
如何避免被目标网站封禁(基础反反爬)
多数小站点没严格风控,但加几条基础头就能绕过最简单的 UA 拦截。别学某些教程发一堆无意义请求头,反而显得可疑。
对于手风琴动画,我们以前分享过很多,有基于jQuery的手风琴菜单,比如jQuery多层级垂直手风琴菜单;也有基于jQuery的手风琴焦点图,比如jQuery实现横向手风琴图片轮播焦点图效果。今天要分享的是一款利用纯CSS3实现的水平手风琴分享按钮菜单,每一个分享按钮展开时会有该平台的简单介绍,非常绚丽实用。
- 必设
User-Agent:用主流浏览器值,比如Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 - 可选加
Accept和Accept-Language,但不是必须;Referer只在需要模拟页面跳转时才设 - 加延时是关键:
await Task.Delay(1000)(1 秒),别用Thread.Sleep阻塞线程 - 不要并发猛刷——单线程 + 延迟,比十个并发还安全
using System; using System.Net.Http; using System.Threading.Tasks; using HtmlAgilityPack;class SimpleCrawler { private static readonly HttpClient client = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
static SimpleCrawler() { client.DefaultRequestHeaders.UserAgent.ParseAdd( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); } public static async Task CrawlAsync(string url) { try { var html = await client.GetStringAsync(url); var doc = new HtmlDocument(); doc.LoadHtml(html); foreach (var link in doc.DocumentNode.SelectNodes("//a[@href]")) { var href = link.GetAttributeValue("href", ""); if (Uri.IsWellFormedUriString(href, UriKind.Absolute)) Console.WriteLine(href); } } catch (HttpRequestException ex) { Console.WriteLine($"Request failed: {ex.StatusCode}"); } }}
真正难的不是发请求或提链接,而是判断哪些 URL 值得跟进、怎么去重、怎么处理跳转和相对路径、何时停——这些逻辑一加上,就不再是“简单”爬虫了。









