使用正则表达式解析HTML:为什么不呢?

nto*_*end 202 regex html-parsing

似乎stackoverflow上的每个问题,其中提问者使用正则表达式从HTML中获取一些信息将不可避免地有一个"答案",表示不使用正则表达式来解析HTML.

为什么不?我知道那里有引用 - 不引用"真正的"HTML解析器,比如Beautiful Soup,我相信它们是强大而有用的,但是如果你只是做一些简单,快速或肮脏的事情,那么为什么当一些正则表达式语句运行得很好时,麻烦​​使用如此复杂的东西?

此外,是否有一些基本的东西,我不了解正则表达式,这使得它们一般是解析的错误选择?

Joh*_*iss 207

正则表达式无法进行整个HTML解析,因为它依赖于匹配开头和结束标记,这是正则表达式无法实现的.

正则表达式只能匹配常规语言,但HTML是无上下文的语言,不是常规语言(正如@StefanPochmann所指出的,常规语言也是无上下文的,因此无上下文并不一定意味着不规则).你可以用HTML上的regexp做的唯一的事情就是启发式,但这并不适用于所有条件.应该可以呈现一个HTML文件,该文件将被任何正则表达式错误地匹配.

  • -1这个答案从错误的参数中得出了正确的结论("用正则表达式解析HTML"这是一个坏主意)("因为HTML不是常规语言").大多数人现在所说的"正则表达式"(PCRE)不仅可以解析无上下文语法(实际上是微不足道的),而且能够解决上下文敏感的语法(参见http://stackoverflow.com/)问题/ 7434272 /比赛的-BN-CN-EG-使用正则表达式,PCRE aaabbbccc-/ 7434814#7434814). (42认同)
  • 到目前为止最佳答案.如果它只能匹配常规语法,那么我们需要一个无限大的正则表达式来解析像HTML这样的无上下文语法.我喜欢这些东西有明确的理论答案. (26认同)
  • 实际上,.Net正则表达式可以在一定程度上使用平衡组和精心设计的表达式来匹配打开和结束标记.在正则表达式中包含_all_的内容当然仍然是疯狂的,它看起来像伟大的代码Chtulhu,并且可能会召唤真正的代码.最后它仍然不适用于所有情况.他们说,如果你编写一个正则表达式,可以正确解析任何HTML,宇宙将崩溃到自身. (5认同)
  • 一些正则表达式库可以做递归正则表达式(有效地使它们成为非正则表达式:) (5认同)
  • 我假设我们正在讨论Perl类型的正则表达式,它们实际上不是正则表达式. (2认同)

kmk*_*lan 35

对于quick'n'dirty regexp会很好.但要知道的根本事情是,构建一个正确解析HTML 的正则表达式是不可能的.

原因是regexp无法处理任意嵌套表达式.请参阅可以使用正则表达式来匹配嵌套模式吗?

  • 一些正则表达式库可以执行递归正则表达式(有效地使它们成为非正则表达式:) (2认同)

And*_*ter 19

(来自http://htmlparsing.com/regexes)

假设您有一个HTML文件,您尝试从<img>标记中提取URL.

<img src="http://example.com/whatever.jpg">
Run Code Online (Sandbox Code Playgroud)

所以你在Perl中写这样的正则表达式:

if ( $html =~ /<img src="(.+)"/ ) {
    $url = $1;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,$url确实会包含 http://example.com/whatever.jpg.但是当你开始像这样开始获取HTML时会发生什么:

<img src='http://example.com/whatever.jpg'>
Run Code Online (Sandbox Code Playgroud)

要么

<img src=http://example.com/whatever.jpg>
Run Code Online (Sandbox Code Playgroud)

要么

<img border=0 src="http://example.com/whatever.jpg">
Run Code Online (Sandbox Code Playgroud)

要么

<img
    src="http://example.com/whatever.jpg">
Run Code Online (Sandbox Code Playgroud)

或者你开始得到误报

<!-- // commented out
<img src="http://example.com/outdated.png">
-->
Run Code Online (Sandbox Code Playgroud)

它看起来很简单,对于一个单一的,不变的文件来说可能很简单,但对于你将要对任意HTML数据做的任何事情,正则表达式只是未来心痛的一个秘诀.

  • 这看起来是真正的答案 - 虽然它可能用正则表达式解析任意HTML,因为今天的正则表达式不仅仅是一个有限的自动机,为了解析任意的html而不仅仅是一个具体的页面,你必须重新实现regexp中的HTML解析器和正则表达式肯定会变得无法辨认1000次. (4认同)
  • 这个答案的推理是*方式*过时,今天比原来应用的更少(我认为它没有).(引用OP:"如果你只是做一些简单,快速或肮脏的事情......".) (2认同)

Han*_*Gay 16

两个快速的原因:

  • 编写一个可以抵御恶意输入的正则表达式很难; 比使用预建工具更难
  • 写一个可以使用你将不可避免地被困的荒谬标记的正则表达式很难; 比使用预建工具更难

关于正则表达式一般用于解析的适用性:它们不适合.您是否见过解析大多数语言所需的各种正则表达式?

  • 基本上所有在运输产品或内部工具中完成的快速和脏的html解析最终都是一个巨大的安全漏洞,或者等待发生的错误.一定要津津乐道.如果可以使用正则表达式,可以使用正确的html解析器. (3认同)
  • 哇?2年后的一次downvote?如果有人想知道,我没有说"因为理论上不可能",因为这个问题清楚地问及"快速而肮脏",而不是"正确".OP显然已经阅读了覆盖理论上不可能的领域的答案,但仍然不满意. (2认同)

Vat*_*ine 16

就解析而言,正则表达式在"词法分析"(lexer)阶段非常有用,其中输入被分解为标记.它在实际的"构建解析树"阶段不太有用.

对于HTML解析器,我希望它只接受格式良好的HTML,并且需要正则表达式之外的功能(它们不能"计数"并确保给定数量的开放元素由相同的数字平衡关闭元素).


Tam*_*ege 8

因为有很多方法可以"搞砸"浏览器会以相当自由的方式处理的HTML,但是需要花费很多精力来重现浏览器的自由行为来覆盖所有具有正则表达式的情况,所以你的正则表达式将不可避免地失败一些特殊的案例,这可能会在您的系统中引入严重的安全漏洞.

  • ntownsend:例如,你认为你已经从HTML中剥离了所有的脚本标签,但你的正则表达式失败涵盖了一个特殊情况(比方说,只适用于IE6):繁荣,你有一个XSS的虚拟性! (4认同)
  • +1提及安全角度.当你与整个互联网连接时,你不能写出hacky"大部分时间都在工作"的代码. (3认同)

oko*_*man 7

问题是,大多数提出与HTML和正则表达式有关的问题的用户都会这样做,因为他们无法找到有效的正则表达式.然后,我们必须考虑在使用DOM或SAX解析器或类似的东西时是否一切都会更容易.它们经过优化和构建,目的是使用类似XML的文档结构.

当然,有些问题可以通过正则表达式轻松解决.但重点在于轻松.

如果您只想查找看起来像http://.../regexp 一样好的所有网址.但是,如果要查找具有类"mylink"的a-Element中的所有URL,则最好使用适当的解析器.


Pet*_*ton 6

正则表达式不是为处理嵌套标记结构而设计的,最好处理所有可能的边缘情况(最糟糕的是,不可能).


tag*_*ers 6

我相信答案在于计算理论.对于使用正则表达式解析的语言,它必须按照定义"常规"(链接).HTML不是常规语言,因为它不符合常规语言的许多标准(与html代码中固有的许多嵌套级别有很大关系).如果您对计算理论感兴趣,我会推荐本书.

  • 我确实读过那本书。我只是没有想到 HTML 是一种上下文无关的语言。 (2认同)

小智 5

HTML/XML 分为标记和内容。正则表达式仅在进行词法标记解析时有用。我想你可以推断出内容。对于 SAX 解析器来说,这将是一个不错的选择。标签和内容可以传递给用户定义的函数,在该函数中可以跟踪元素的嵌套/关闭。

至于只是解析标签,它可以用正则表达式完成,并用于从文档中去除标签。

经过多年的测试,我发现了浏览器解析标签的方式的秘密,无论是好的还是坏的。

普通元素用这种形式解析:

这些标签的核心使用这个正则表达式

 (?:
      " [\S\s]*? " 
   |  ' [\S\s]*? ' 
   |  [^>]? 
 )+
Run Code Online (Sandbox Code Playgroud)

您会注意到这[^>]?是其中一种变化。这将匹配来自格式错误的标签的不平衡引号。

它也是正则表达式最邪恶的根源。它的使用方式将触发一次碰撞以满足它贪婪的、必须匹配的量化容器。

如果被动使用,则永远不会有问题但是,如果您通过将某些东西与所需的属性/值对穿插来强制匹配,并且没有提供足够的防止回溯的保护,那将是一场失控的噩梦。

这是普通旧标签的一般形式。注意到 [\w:]代表标签名称了吗?实际上,代表标签名称的合法字符是一个令人难以置信的 Unicode 字符列表。

 <     
 (?:
      [\w:]+ 
      \s+ 
      (?:
           " [\S\s]*? " 
        |  ' [\S\s]*? ' 
        |  [^>]? 
      )+
      \s* /?
 )
 >
Run Code Online (Sandbox Code Playgroud)

继续前进,我们还看到您无法在不解析所有标签的情况下搜索特定标签。我的意思是你可以,但它必须使用像 (*SKIP)(*FAIL) 这样的动词组合,但仍然必须解析所有标签。

原因是标签语法可能隐藏在其他标签中,等等。

因此,要被动地解析所有标签,需要像下面这样的正则表达式。这个特定的内容也匹配不可见的内容

当新的 HTML 或 xml 或任何其他开发新结构时,只需将其添加为替代之一。


网页说明 - 我从未见过有问题的网页(或 xhtml/xml)
。如果你找到了,请告诉我。

性能说明 - 很快。这是我见过的最快的标签解析器
(可能更快,谁知道呢)。
我有几个特定的​​版本。它也非常适合用作刮刀
(如果您是动手型的)。


完整的原始正则表达式

<(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\s+(?>"[\S\s]*?"|'[\S\s]*?'|(?:(?!/>)[^>])?)+)?\s*>)[\S\s]*?</\1\s*(?=>))|(?:/?[\w:]+\s*/?)|(?:[\w:]+\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)+\s*/?)|\?[\S\s]*?\?|(?:!(?:(?:DOCTYPE[\S\s]*?)|(?:\[CDATA\[[\S\s]*?\]\])|(?:--[\S\s]*?--)|(?:ATTLIST[\S\s]*?)|(?:ENTITY[\S\s]*?)|(?:ELEMENT[\S\s]*?))))>

格式化外观

 <
 (?:
      (?:
           (?:
                # Invisible content; end tag req'd
                (                             # (1 start)
                     script
                  |  style
                  |  object
                  |  embed
                  |  applet
                  |  noframes
                  |  noscript
                  |  noembed 
                )                             # (1 end)
                (?:
                     \s+ 
                     (?>
                          " [\S\s]*? "
                       |  ' [\S\s]*? '
                       |  (?:
                               (?! /> )
                               [^>] 
                          )?
                     )+
                )?
                \s* >
           )

           [\S\s]*? </ \1 \s* 
           (?= > )
      )

   |  (?: /? [\w:]+ \s* /? )
   |  (?:
           [\w:]+ 
           \s+ 
           (?:
                " [\S\s]*? " 
             |  ' [\S\s]*? ' 
             |  [^>]? 
           )+
           \s* /?
      )
   |  \? [\S\s]*? \?
   |  (?:
           !
           (?:
                (?: DOCTYPE [\S\s]*? )
             |  (?: \[CDATA\[ [\S\s]*? \]\] )
             |  (?: -- [\S\s]*? -- )
             |  (?: ATTLIST [\S\s]*? )
             |  (?: ENTITY [\S\s]*? )
             |  (?: ELEMENT [\S\s]*? )
           )
      )
 )
 >
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

58767 次

最近记录:

7 年,9 月 前