如果 HTML 解析不使用正则表达式,它们如何工作?

How do HTML parses work if they're not using regexp?

提问人:Andy E 提问时间:3/8/2010 最后编辑:Martin.Andy E 更新时间:4/13/2012 访问量:6727

问:

我每天都会看到一些问题,询问如何从一些 HTML 字符串中解析或提取一些东西,第一个答案/评论总是“不要使用正则表达式来解析 HTML,以免你感到愤怒!(最后一部分有时会省略)。

这让我相当困惑,我一直认为,一般来说,解析任何复杂字符串的最佳方法是使用正则表达式。那么 HTML 解析器是如何工作的呢?它不是使用正则表达式来解析吗?

使用正则表达式的一个特殊论点是,并不总是有解析替代方案(例如 JavaScript,其中 DOMDocument 不是通用选项)。例如,jQuery 似乎可以很好地使用正则表达式将 HTML 字符串转换为 DOM 节点。

不确定是否要CW这个,这是一个真正的问题,我想得到回答,而不是真正打算成为一个讨论线程。

则表达 html 解析

评论


答:

3赞 Timothy Khouri 3/8/2010 #1

如果你想有一个 100% 的解决方案:你需要编写自己的自定义代码,逐个字符迭代 HTML,你需要有大量的逻辑来确定你是否应该停止当前节点并启动下一个节点。

原因是这是有效的 HTML:

<ul>
<li>One
<li>Two
<li>Three
</ul>

但这也是:

<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>

如果您对“90% 解决方案”感到满意:那么使用 XML 解析器加载文档就可以了。或者使用正则表达式(尽管如果您掌握了内容,则 xml 会更容易)。

评论

0赞 Andy E 3/8/2010
当然可以,但是逐个字符地遍历文档字符以测试大量逻辑不是很慢吗?当然,浏览器是 100% 的解决方案(尽管我猜不是因为它们的解析方式不同,而是足够接近),它们是否像那样遍历文档?
4赞 Quentin 3/8/2010
XML 解析器更像是 1% 的解决方案。格式正确的 XML 的 HTML 文档的数量很少。
4赞 Timothy Khouri 3/8/2010
是的,他们确实...不要从字面上理解“逐个角色”,因为您可以尝试流式传输内容。但我的观点是,你必须编写自己的解析器。新时代的程序员不习惯编写这种代码......我们习惯了“HtmlDocumentUtility.Load”之类的东西:)
4赞 Bart van Heukelom 3/8/2010
@Andy E:正则表达式不是魔术,它们也可以逐个字符工作,就像任何其他类型的解析,或者哎呀,任何其他字符串函数一样。
2赞 Andy E 3/8/2010
@Bart:说得好,有时我的大脑会忘记所有的逻辑,认为事情是靠魔法运作的。
66赞 Quentin 3/8/2010 #2

通常通过使用分词器。HTML5 规范草案有一个广泛的算法来处理“现实世界的 HTML”。

评论

1赞 Timothy Khouri 3/8/2010
好找...引用“为了处理这些情况,解析器有一个脚本嵌套级别,最初必须设置为零,以及一个解析器暂停标志,最初必须设置为 false。 - 换句话说,你必须自己迭代它,并有很多自定义逻辑:P
1赞 Quentin 3/8/2010
使用大量自定义逻辑自己迭代它并不是一个好主意。如果可以,请使用支持标准算法的库。例如 search.cpan.org/~tobyink/HTML-HTML5-Parser-0.03/lib/HTML/HTML5/... / code.google.com/p/html5lib
0赞 Timothy Khouri 3/8/2010
@David - 这是真的,但他的问题是“HTML 解析器是如何编写的”......所以我回答了这个问题(你更清楚地说“通常使用标记器”)。
8赞 SF. 3/8/2010
HTML 解析器的主要问题是,在遇到错误时,您不能吐出“解析错误”并保留它。你进入怪癖模式,试图从你遇到的混乱中找出最好的,包括不匹配的标签、[{]} 风格的隔行扫描,以及各种奇怪的事情,试图让结果看起来尽可能好,而不可避免的失败是最不痛苦的......这不是您可以使用正则表达式执行的操作。
7赞 JXG 3/8/2010
@Timothy K:“注意:由于该算法导致元素改变父母的方式,它被称为”收养机构算法“(与其他可能用于处理错误嵌套内容的算法形成鲜明对比,其中包括”算法“、”秘密事务算法“和”海森堡算法“)。
22赞 T.J. Crowder 3/8/2010 #3

正则表达式只是解析器的一种形式。一个诚实至善的 HTML 解析器将比在正则表达式中表达的复杂得多,它使用递归下降、预测和其他几种技术来正确解释文本。如果你真的想进入它,你可以看看lex和yacc和类似的工具。

禁止使用正则表达式进行 HTML 解析可能应该更正确地写成:“不要使用朴素的正则表达式来解析 HTML......”(免得你们感到忿怒)"...并谨慎对待结果。对于某些特定目标,正则表达式可能完全足够,但您需要非常小心地了解正则表达式的局限性,并尽可能谨慎地解析您正在解析的文本源(例如,如果是用户输入,请非常小心)。

评论

0赞 Andy E 3/8/2010
+1,一个很好的答案。我必须承认,即使我无法控制 HTML,我以前也使用过正则表达式,但在任何公开发布的应用程序中都没有使用过。我也确实“感到愤怒”,因为它太天真了。但那是很久以前的事了:-)
136赞 JXG 3/8/2010 #4

那么 HTML 解析器是如何工作的呢?它不是使用正则表达式来解析吗?

嗯,不。

如果你回到你的大脑中,如果你上了一门计算理论课程,或者一门编译器课程,或者类似的东西,你可能会记得有不同种类的语言和计算模型。我没有资格详细介绍,但我可以和你一起回顾一些要点。

最简单的语言和计算类型(用于这些目的)是常规语言。这些可以用正则表达式生成,也可以用有限自动机来识别。基本上,这意味着在这些语言中“解析”字符串使用状态,而不是辅助内存。HTML 当然不是一种常规语言。如果你仔细想想,标签列表可以任意嵌套。例如,表可以包含表,每个表可以包含大量嵌套标记。使用正则表达式,您也许可以挑选出一对标签,但肯定不能任意嵌套任何标签。

一种不规则的经典简单语言是正确匹配的括号。尽你所能,你永远无法构建一个永远有效的正则表达式(或有限自动机)。您需要内存来跟踪嵌套深度。

具有内存堆栈的状态机是计算模型的下一个优势。这称为下推自动机,它识别由上下文无关语法生成的语言。在这里,我们可以识别正确匹配的括号——事实上,堆栈是它的完美内存模型。

那么,这对 HTML 来说足够好吗?可悲的是,没有。也许对于超级骗子来说,实际上,经过仔细验证的XML,其中所有标签总是完美地排列在一起。在现实世界的 HTML 中,您可以轻松找到类似 .这显然没有嵌套,所以为了正确解析它,堆栈不够强大。<b><i>wow!</b></i>

下一个计算级别是由通用语法生成并由图灵机识别的语言。这被普遍认为是最强大的计算模型——一台带有辅助内存的状态机,其内存可以在任何地方修改。这就是编程语言可以做到的。这就是 HTML 所处的复杂程度。

用一句话总结这里的一切:要解析通用 HTML,您需要一种真正的编程语言,而不是正则表达式。

HTML 的解析方式与其他语言的解析方式相同:词法分析和解析。词法转换步骤将单个字符的流分解为有意义的标记。分析步骤使用状态和内存将标记组合成一个逻辑上一致的文档,可以对其进行操作。

6赞 Svante 3/8/2010 #5

解析 HTML 是将线性文本转换为树状结构。正则表达式通常不能处理树结构。获取下一个令牌所需的每个点的正则表达式一直在变化。您可以在解析器中使用正则表达式,但对于每种可能的解析状态,您将需要一整套正则表达式。