首页 文章

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

提问于
浏览
95

我每天都会看到问题,询问如何从某些HTML字符串中解析或提取某些内容,第一个答案/注释始终是“不要使用RegEx解析HTML,以免感到愤怒!” (有时省略最后一部分) .

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

使用正则表达式的一个特殊参数是,并不总是有一种解析替代方法(例如JavaScript,其中DOMDocument不是普遍可用的选项) . 例如,jQuery似乎可以使用正则表达式将HTML字符串转换为DOM节点 .

不知道是否CW这个,这是一个真正的问题,我想要回答,而不是真正想成为一个讨论主题 .

5 回答

  • 2

    通常使用tokeniser . 草案HTML5 specification has an extensive algorithm用于处理"real world HTML" .

  • 130

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

    好吧,不 .

    如果你回到脑中学习计算理论课程,如果你选择了一门课程,或者编写了一个编程课程,或者类似的东西,你可能会记得有不同种类的语言和计算模型 . 我没有资格详细介绍所有细节,但我可以和你一起回顾几个要点 .

    最简单的语言和计算类型(用于这些目的)是一种常规语言 . 这些可以使用正则表达式生成,并使用有限自动机进行识别 . 基本上,这意味着这些语言中的“解析”字符串使用状态,而不是辅助存储器 . HTML当然不是常用语言 . 如果你考虑一下,标签列表可以任意嵌套 . 例如,表可以包含表,每个表可以包含许多嵌套标记 . 使用正则表达式,您可以选择一对标签,但肯定不能任意嵌套 .

    非常规的经典简单语言是正确匹配的括号 . 尽量尝试,你将永远无法构建一个永远有效的正则表达式(或有限自动机) . 你需要记忆来跟踪嵌套深度 .

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

    嗯,这对HTML来说还算不错吗?可悲的是没有 . 也许对于超级经验丰富的经过验证的XML,实际上,所有标签总是完美排列 . 在真实世界的HTML中,您可以轻松找到像 <b><i>wow!</b></i> 这样的代码段 . 这显然不会嵌套,所以为了正确解析它,堆栈不够强大 .

    下一级计算是由一般语法生成的语言,并由图灵机器识别 . 这通常被认为是有效的最强计算模型 - 具有辅助存储器的状态机,其存储器可以在任何地方进行修改 . 这就是编程语言可以做的事情 . 这是HTML所处的复杂程度 .

    用一句话概括这里的所有内容:要解析一般HTML,你需要一种真正的编程语言,而不是正则表达式 .

    HTML的解析方式与解析其他语言的方式相同:lexing和parsing . lexing步骤将单个字符流分解为有意义的标记 . 解析步骤使用状态和内存将令牌组装成可以被操作的逻辑连贯文档 .

  • 22

    正则表达式只是解析器的一种形式 . 诚实的HTML解析器将比正则表达式复杂得多,使用recursive descent,预测和其他几种技术来正确解释文本 . 如果你真的想进入它,你可以查看lex & yacc和类似的工具 .

    禁止使用正则表达式进行HTML解析应该更正确地写为:“不要使用天真的正则表达式解析HTML ...”(以免你感到愤怒)"...and treat the results with caution."对于某些特定的目标,正则表达式可能是完美的足够了,但是你需要非常小心地注意你的正则表达式的局限性,并且要谨慎对待用户输入的文本来源,要非常小心确实) .

  • 64

    解析HTML是将线性文本转换为树结构 . 正则表达式通常不能处理树结构 . 每个点所需的正则表达式可以随时更改下一个标记 . 您可以在解析器中使用正则表达式,但是对于每种可能的解析状态,您将需要一整套正则表达式 .

  • 6

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

    原因是这是有效的HTML:

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

    但这也是这样的:

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

    如果您对“90%解决方案”没问题:那么使用XML解析器加载文档就可以了 . 或者使用Regex(尽管如果你是内容的主人,xml会更容易) .

相关问题