htmlagilitypack解析html需先校验documentnode非空、html结构完整及编码正确;xpath应避免精确匹配class,改用contains;须移除script/style节点防乱码;远程加载需显式设置编码。

直接用 HtmlAgilityPack 解析 HTML 是可行的,但不加防护地调用 SelectNodes() 或 SelectSingleNode() 会高频崩溃——最常见原因是 DocumentNode 为 null,而不是 HTML 写得不对。
HtmlDocument.LoadHtml() 后 DocumentNode 为 null 怎么办
这不是解析失败,而是你传了空、null、网络错误响应或无根节点的 HTML 片段。它不会抛异常,只会让 DocumentNode == null,后续所有节点操作都崩。
- 永远在解析后加判断:
if (doc.DocumentNode == null) throw new InvalidOperationException("HTML is empty or malformed"); - 用
LoadHtml(htmlString?.Trim())前先校验:string.IsNullOrWhiteSpace(htmlString) - 从
HttpClient拿内容后,别直接丢给LoadHtml():先检查response.IsSuccessStatusCode和html?.Length > 0 - 解析纯片段(比如只有
<div>xxx</div>)时,手动包裹一层:html = $"{html}";
SelectNodes("//div[@class='content']") 返回 null 或空集合
XPath 表达式本身没错,但 HTML 不是 XML:class 属性可能含多个值、引号不统一、大小写混用,甚至根本没引号——HtmlAgilityPack 能容错解析,但 XPath 查询引擎不会帮你“猜”语义。
- 别依赖
@class='content',改用contains(@class, 'content');但注意误匹配(如'highlighted'也会被contains(@class, 'light')命中) - 要精准匹配多 class,先用
GetAttributeValue("class", "")取出字符串,再用.Split().Contains("content")判断 -
SelectNodes()不支持 CSS 选择器,"div.content"会静默返回 null,必须写成"//div[contains(@class, 'content')]" - 调试时立刻输出
doc.DocumentNode.OuterHtml.Substring(0, 500),确认实际 DOM 结构是否和你看到的浏览器源码一致
InnerText 提取到 script/style 里的内容或乱码
InnerText 是按 DOM 树遍历文本节点,不区分语义——<script>alert('x')</script> 里的字符串照样算“文本”。中文乱码则几乎全是编码没对齐导致的。
- 提取前先移除无关节点:
doc.DocumentNode.SelectNodes("//script | //style")?.ToList().ForEach(n => n.Remove()); - 用
HtmlWeb加载远程 URL 时,必须设web.OverrideEncoding = Encoding.UTF8;,否则默认按系统编码(如 GB2312)解码 UTF-8 字节流 - 用
LoadHtml()时,确保输入的string已是正确解码后的结果;若原始是byte[],请先用Encoding.UTF8.GetString(bytes) -
PreserveWhitespace = true会影响换行和缩进显示,有时看起来像“丢字”,其实是空白字符被保留后渲染异常
真正难的不是写对 XPath,而是接受 HTML 的“脏”:没有规范闭合、属性随意、嵌套错乱。别指望一次写对 selector 就万事大吉,DocumentNode 是否为空、实际 DOM 结构是否如你所见、编码是否对齐——这三个点漏掉任何一个,都会让爬虫在生产环境静默失败。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











