HttpClient 是下载网页 HTML 的首选,需设 User-Agent、用 await GetStringAsync、处理 GB2312 编码;解析用 HtmlAgilityPack 提取 img[src] 及 data-src 等属性,转绝对路径后流式下载,并加超时、限速、UA 轮换与随机延迟防风控。
用 HttpClient 下载网页 HTML 再解析,别用 WebClient 或 IE 控件
直接抓图片的前提是先拿到网页源码,httpclient 是目前最稳妥的选择。webclient 已标记为过时,而 webbrowser 依赖 ie 引擎、启动慢、线程模型复杂,容易卡死或抛 comexception。
常见错误是没设请求头,被网站反爬直接返回空页或 403 —— 至少加上 User-Agent:
var client = new HttpClient();
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
- 务必用
await client.GetStringAsync(url),别用同步的GetAsync().Result,否则容易死锁 - 如果页面是动态渲染(如 Vue/React),
HttpClient拿到的是初始 HTML,不含 JS 渲染后的<img>,这时得换 PuppeteerSharp - 注意编码:部分老站用 GB2312,
GetStringAsync可能乱码,需用GetByteArrayAsync+Encoding.GetEncoding("gb2312").GetString(...)
用 HtmlAgilityPack 解析 <img> 标签,别硬写正则
正则匹配 <img src="..."> 看似简单,但实际会漏掉 srcset、data-src(懒加载)、background-image 行内样式,还容易被注释、嵌套引号、换行搞崩。真实网页里 <img> 属性顺序不固定、有大小写混用、甚至带命名空间 —— 正则根本不可靠。
HtmlAgilityPack 是 .NET 生态里最成熟的 HTML 解析库,它把 HTML 当树处理,稳定且语义清晰:
var doc = new HtmlDocument();
doc.LoadHtml(html);
var imgs = doc.DocumentNode.SelectNodes("//img[@src]");
- 取
src:直接img.GetAttributeValue("src", "") - 兼容懒加载:顺手加一句
|| img.GetAttributeValue("data-src", "") || img.GetAttributeValue("data-original", "") - 过滤无效链接:跳过
javascript:、about:、空字符串、data:image/base64 - 注意相对路径:要用
new Uri(baseUri, src)转成绝对地址,baseUri是原始网页 URL
下载图片时用 HttpClient 流式写入,避免内存爆掉
一张 5MB 的图,用 GetStringAsync 或 GetByteArrayAsync 会一次性全读进内存;批量下载几十张就可能触发 OutOfMemoryException。
正确做法是用 GetStreamAsync + FileStream 分块拷贝:
using var stream = await client.GetStreamAsync(imgUrl); using var file = File.Create(filePath); await stream.CopyToAsync(file);
- 加超时控制:
client.Timeout = TimeSpan.FromSeconds(15),否则卡在某个坏链接上整个任务挂住 - 并发数别瞎开高:默认
HttpClient连接池上限是 2,想并发下载得显式配置SocketsHttpHandler.MaxConnectionsPerServer - 文件名要 sanitize:
Path.GetInvalidFileNameChars()里的字符(如<, >, :)必须替换或移除,否则UnauthorizedAccessException
遇到 429 Too Many Requests 或验证码,说明你已触发风控
这不是代码 bug,是策略问题。很多站点对无头请求频率敏感,哪怕只抓一页,也可能被限流。
- 加随机延迟:
await Task.Delay(Random.Shared.Next(500, 2000)),别用固定值 - 换 User-Agent 池:每次请求换一个,从常见浏览器 UA 列表里随机取
- 别省略
Accept、Accept-Language等头,越像真实浏览器越安全 - 如果目标站用了 Cloudflare 或人机验证,
HtmlAgilityPack+HttpClient就不够用了,得切到PuppeteerSharp或找 API 接口
真正难的从来不是“怎么写出第一张图”,而是“怎么让一百张图都稳稳落盘还不被封 IP”。路径、编码、UA、延迟、异常恢复——每个环节松一扣,整条链就断。


















