【问题标题】:Parsing HTML - is regex the only option in this case?解析 HTML - 在这种情况下,正则表达式是唯一的选择吗?
【发布时间】:2011-06-10 15:20:15
【问题描述】:

用户将提供 HTML,它可能是有效的或无效的(格式错误)。我需要能够确定以下内容:

  1. 正文中是否有样式标签
  2. 是否存在具有使用宽度或背景图像的样式属性的 div。

我尝试过使用 DOMDocument 类,但它只能用 xPath 做 1 而不能做 2。

我也试过simple_html_dom,只能做1,不能做2。

你认为我只使用正则表达式是个好主意还是有什么我没有想到的?

【问题讨论】:

  • 他们都可以做 2,但需要注意的是您必须自己解析 style 属性。 部分你可以使用正则表达式。
  • 格式有多畸形?有 HTML 解析器,独立于 XML 解析器,它们倾向于解决不允许使用 XML 解析器解析 HTML 的问题。
  • @Abs:因为正则表达式单行 style 属性与正则表达式 HTML 文档完全不同。您不妨问我,“如果我可以正则表达式 style 属性,为什么我不能正则表达式巨型河马?”这是完全不合逻辑的。
  • 此 XPath 不能用于第二个选项://div[contains(@style,'width:') or contains(@style,'background-image:')

标签: php html regex parsing


【解决方案1】:

Regex 是 NEVER(同样:NEVER!)解析 HTML 的解决方案!

正则表达式可用于 Type-3 Chomsky 语言(常规语言)。
然而,HTML 是一种 Type-2 Chomsky 语言(上下文无关语言)。

如果仍有疑问:http://en.wikipedia.org/wiki/Chomsky_hierarchy#The_hierarchy

要安全地使用 type-2 语言,您需要一个上下文无关的语言解析器。 您可能想尝试使用 LL 解析器或递归下降解析器,例如


话虽这么说:

bodystyle 匹配:

<body\s+[^>]*style\s*=\s*["'].*?[^"']*?["'][^>]*>

style 中匹配divwidth|background-image

<div\s+[^>]*style\s*=\s*["'][^"']*?(width|background-image)[^"']*?["'][^>]*>

他们都错误匹配所说的标签如果被注释掉(这就是为什么我说不可能)。

【讨论】:

  • @Regexident 大声笑我明白了,我知道这部分我在使用 PHP dom 类时找不到 2 的解决方案。好的,你能说出我可以在 PHP 中使用的东西吗?
  • 是的,但不要永远做这样的概括!见here
  • @tomalak-geretkal:好吧,如果您使用正则表达式来解析 HTML 代码的一个非常有限的子集,那么您可以使用正则表达式。但话又说回来:您不再处理 HTML。相反,您正在处理与 type-3 兼容的 HTML 子集。就语言理论而言,这是完全不同的事情。
  • @Abs:尝试使用 HTML DOM-tree 解析器,例如 libxml,或任何你为 PHP 找到的东西。
  • @Regexident:我知道。抛开理论不谈,在现实世界中,为了履行合同需要解决实际问题,这有时完全没问题。
【解决方案2】:

XPath 可以同时执行 (1) 和 (2):

测试body中是否有style标签:

//body//style

使用widthbackground-image 测试是否有带有样式属性的div:

//div[contains(@style,'width:') or contains(@style,'background-image:')]

而且,正如您在 cmets 中好奇的那样,查看样式标签是否包含 a:hoverfont-size

//style[contains(text(),'a:hover') or contains(text(),'font-size:')]

【讨论】:

  • 你说得对,XPath 可以满足我的需求。感谢 Austin 阅读了我的问题,并且没有陷入关于是否最好使用正则表达式进行 HTML 解析的争论!
  • @Abs - 您不想使用正则表达式进行 HTML 解析的原因是它无法处理其中固有的递归性。如果您只是想查看页面上是否有宽度样式的 div 或正文中是否有样式标签,那没关系,但要真正从文档中提取信息需要真正的解析器,而不是正则表达式。
【解决方案3】:

您可以使用 Tidy 清理 HTML,然后将其解析为 XML。然后很容易使用 xpath 来查找节点。试试这样的:

$tidyConfig = array(
    "add-xml-decl" => true,
    "output-xml" => true,
    "numeric-entities" => true
);
$tidy = new tidy();
$tidy->parseString($html, $tidyConfig, "utf8");
$tidy->cleanRepair();
$xml = new SimpleXMLElement($tidy);
$matches = $xml->xpath('style');

至于解析样式属性以查找特定选择器,我认为您必须手动执行此操作。如果需要,您可以使用CSS parser

【讨论】:

  • Tidy 不是一个选项,我试过了,我发现它改变了原始 HTML,我需要保持 HTML 原样并解析它。所以我需要一些能让我解析无效 HTML 的东西。
  • 你是什么意思它改变了原来的?每当您解析无效的 HTML 时,它都有可能以与您预期的方式不同的方式对其进行解释。不知道如何避免这种情况。
  • I find that it changes the original HTML 是的,这就是它的工作。你还期待它做什么?第一步让 Tidy 修复您的输入(您的原始数据源将不受影响),然后继续。
  • @Abs:“解析无效的 HTML”是不可能的。解析需要知道输入数据是什么样的,以及它的结构和规则是什么。如果您的输入无效(以不可预测的方式),那么您没有语法,并且无法解析。这就像试图用你不懂的语言阅读一本书。
  • 如果您将一个无效的 HTML 文档与同一个无效的 HTML 文档进行比较,并且您通过了 tidy。它们将是两个不同的文件。例如,如果你在 body 中有一个 style 标签,tidy 会将它移动到 head 部分。我不想发生这种变化。
【解决方案4】:

It's rarely a good idea to parse HTML with regex。但是,任何好的 HTML 解析器都能够找到所有带有 style 标签的 divs,一旦你完成了,正则表达式对于解析样式属性很有用。

然而,复杂(但有效)的 CSS 仍然有可能破坏大多数正则表达式,因此这里真正持久的东西是 HTML 解析器与 CSS 解析器的结合。不过,这可能有点矫枉过正。像 \bwidth\s*:\s*(\w+) 这样的正则表达式可能会捕获任何 width 值,除非有人在积极地试图愚弄它。

编辑:

一个好的 HTML 解析器不会阻塞任何不会阻塞浏览器的东西。我不再是 PHP 人了,但我听说过一些关于 HTML Purifier 的好消息。

【讨论】:

  • 说实话,我需要做的就是找到元素的存在以及它们是否具有某些属性。这对于正则表达式应该没问题。事实上,我会将 HTML 用作字符串。
  • @Abs - 正则表达式不适用于整个 HTML 字符串。至少,它甚至不会接近可靠,而不是没有大量的工作。但是,我会继续使用我在第一段中提到的方法:使用 DOM 解析器从每个 div 中获取 style 属性,然后对其运行正则表达式。相信我,这将比使用正则表达式完成整个事情更容易。
  • @Abs - 编辑建议一个好的 HTML 解析器,如果你的没有削减它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多