必须用 htmlagilitypack 等专业解析器而非正则处理 html,因 html 是上下文相关语言,正则无法正确处理嵌套、转义、大小写不敏感、注释、cdata 等问题;仅在完全可控的单层纯文本场景才可谨慎使用正则。

别用正则解析 HTML。它不是设计来干这个的,硬上会掉进嵌套、转义、标签不闭合、命名空间、HTML5 自定义属性等一堆坑里,看似能跑通简单例子,一到真实网页就崩。
为什么 Regex 解析 HTML 总是“差一点”
HTML 是上下文相关语言,而正则表达式本质是处理正则文法(上下文无关)。比如:<div><p>hello</p></div> 看似能用 Regex.Match(@"<div>(.*?)</div>", html) 提取,但只要中间出现 <div class="nested"></div>,捕获组就会在第一个 就结束——根本无法识别嵌套层级。
常见错误现象包括:
- 匹配到一半的标签,比如
<img src="a.jpg" alt="quote" end>中的引号转义导致正则提前截断 - 忽略大小写差异(
<div> 和 <code><div>),而 HTML 是大小写不敏感的<li>把注释 <code><!-- comment -->、CDATA 段或 script/style 内容也当普通标签处理
真要快速提取,用 HtmlAgilityPack 替代 Regex
这是 .NET 生态最成熟、零依赖、支持 malformed HTML 的解析库。它把 HTML 当成 DOM 树处理,语义清晰、容错强、可读性高。
安装后(NuGet Install-Package HtmlAgilityPack),典型用法如下:
var doc = new HtmlDocument();
doc.LoadHtml(htmlString); // 自动修复破损结构
var titleNode = doc.DocumentNode.SelectSingleNode("//title");
string title = titleNode?.InnerText.Trim();
var links = doc.DocumentNode.SelectNodes("//a[@href]");
foreach (var link in links)
{
string href = link.GetAttributeValue("href", "");
string text = link.InnerText.Trim();
}
关键点:
-
SelectSingleNode和SelectNodes支持 XPath,比手写正则直观得多 - 对自闭合标签(
<img>)、无引号属性(<a href="xxx"></a>)、大小写混用全兼容 - 性能足够日常使用;若需极致速度且 HTML 结构极固定,才考虑用
Span<char></char>+ 手动扫描,而非正则
如果非要用正则(比如纯文本片段、可控格式)
仅限于:你完全控制输入格式,且只提取单层、无嵌套、无转义、无注释的简单字段。例如日志中固定的 [url:https://example.com] 或模板里的 {{title}}。
此时注意:
- 避免
.*?跨行匹配,改用[\s\S]*?或设置RegexOptions.Singleline - 属性值提取必须区分单/双/无引号:用
href\s*=\s*(["']?)([^"'>\s]+)\1,其中\1确保引号配对 - 永远加
RegexOptions.IgnoreCase,HTML 标签名不区分大小写 - 不要试图用一个正则匹配整个标签树——拆成多个小目标,比如先抽所有
<a></a>开始标签,再单独处理属性
真正难的从来不是“怎么写正则”,而是判断“该不该用正则”。遇到任意含嵌套、用户生成、第三方来源的 HTML,HtmlAgilityPack 就是默认答案。手动轮子造得再快,也扛不住浏览器实际渲染时那点细微的 tag 修复逻辑。











