PHP 中的容错 HTML/XML/SGML 解析
Posted
技术标签:
【中文标题】PHP 中的容错 HTML/XML/SGML 解析【英文标题】:Error Tolerant HTML/XML/SGML parsing in PHP 【发布时间】:2010-09-09 03:58:36 【问题描述】:我有一堆类似 html 的遗留文档。例如,它们看起来像 HTML,但有额外的不属于 HTML 的组成标签
<strong>This is an example of a <pseud-template>fake tag</pseud-template></strong>
我需要解析这些文件。 php 是唯一可用的工具。这些文档并不接近于格式良好的 XML。
我最初的想法是在 PHP DOMDocument 上使用 loadHTML 方法。但是,这些方法会阻塞 HTML 标签,并且会拒绝解析字符串/文件。
$oDom = new DomDocument();
$oDom->loadHTML("<strong>This is an example of a <pseud-template>fake tag</pseud-template></strong>");
//gives us
DOMDocument::loadHTML() [function.loadHTML]: Tag pseud-template invalid in Entity, line: 1 occured in ....
我能想到的唯一解决方案是使用字符串替换函数对文件进行预处理,该函数将删除无效标签并用有效的 HTML 标签替换它们(可能是带有标签 id 的跨度名称)。
有没有更优雅的解决方案?一种让 DOMDocument 知道其他标签被认为是有效的方法?是否有针对 PHP 的不同的、强大的 HTML 解析类/对象?
(如果不是很明显,我不认为正则表达式是一个有效的解决方案)
更新:虚假标签中的信息是这里目标的一部分,因此不能选择 Tidy。另外,我正在寻找可以为我完成某种程度(如果不是全部)格式良好的清理工作,这就是我首先查看 DomDocument 的 loadHTML 方法的原因。
【问题讨论】:
【参考方案1】:我想知道通过HTML Tidy 传递“坏”HTML 是否有助于第一次传递?可能值得一看,如果您可以使文档格式良好,也许您可以使用 DomDocument 将其作为常规 XML 文件加载。
【讨论】:
抱歉,我应该更具体一点,我需要从文件中解析出的部分内容是在假标签中发现的内容。 我建议将 HTMLTidy 作为预处理步骤来尝试获得格式良好的 XML,然后您可以使用 DomDocument 解析它并读取整个 DOM,如果运气好的话 :) 不整洁地去除虚假标记以及它所做的所有重新格式化吗?【参考方案2】:@Twan
您不需要 DOMDocument 的 DTD 来解析自定义 XML。只要使用DOMDocument->load()
,只要XML格式正确,就可以读取。
一旦您的文件格式正确,您就可以开始研究 XML 解析器,在此之前您就是 S.O.L. Lok Alejo 说,你可以看看 HTML TIDY,但它看起来是 HTML 特有的,我不知道它会如何与你的自定义元素搭配使用。
我不认为正则表达式是一个有效的解决方案
在您形成良好的状态之前,这可能是您唯一的选择。一旦你把文档放到那个阶段,那么你就可以清楚地使用 DOM 函数了。
【讨论】:
当您使用 DOMDocument 加载 HTML 文件时,它似乎会进行某种程度的清理:格式良好,但要求您的所有标签都是合法的 HTML 标签。我正在寻找能做到前者的东西,但不是后者。【参考方案3】:看看 PHP Fit 端口中的 Parser。代码很干净,最初是为加载 Word 保存的脏 HTML 而设计的。它被配置为拉出表格,但很容易适应。
您可以在此处查看源代码: http://gerd.exit0.net/pat/PHPFIT/PHPFIT-0.1.0/Parser.phps
单元测试将向您展示如何使用它: http://gerd.exit0.net/pat/PHPFIT/PHPFIT-0.1.0/test/parser.phps
【讨论】:
【参考方案4】:我对这个问题的快速而肮脏的解决方案是运行一个循环,将我的自定义标签列表与正则表达式匹配。正则表达式不会捕获其中包含另一个内部自定义标签的标签。
当匹配时,会调用处理该标签的函数并返回“处理后的 HTML”。如果该自定义标签在另一个自定义标签中,那么由于实际 HTML 被插入到子标签的位置,父标签就变成了无子标签,并且它将被正则表达式匹配并在循环的下一次迭代中处理。
当没有要匹配的无子自定义标签时,循环结束。总的来说,它是迭代的(while 循环)而不是递归的。
【讨论】:
【参考方案5】:@艾伦风暴
你对我其他答案的评论让我开始思考:
当您使用 DOMDocument 加载 HTML 文件时,它似乎会进行某种程度的清理:格式良好,但要求您的所有标签都是合法的 HTML 标签。我正在寻找可以做到前者的东西,但不是后者。 (艾伦风暴)
在标签上运行一个正则表达式(对不起!),当它发现一个不是有效的 HTML 元素时,用你知道在任何文档中都不存在的有效元素替换它 (blink
想到...),并给它一个带有非法元素名称的属性值,以便您以后可以将其切换回来。例如:
$code = str_replace("<pseudo-tag>", "<blink rel=\"pseudo-tag\">", $code);
// and then back again...
$code = preg_replace('<blink rel="(.*?)">', '<\1>', $code);
显然该代码行不通,但您明白了吗?
【讨论】:
【参考方案6】:您可以在加载文档时使用libxml_use_internal_errors
禁止警告。例如:
libxml_use_internal_errors(true);
$doc = new DomDocument();
$doc->loadHTML("<strong>This is an example of a <pseud-template>fake tag</pseud-template></strong>");
libxml_use_internal_errors(false);
如果由于某种原因您需要访问警告,请使用libxml_get_errors
【讨论】:
你应该等几个星期,你本可以得到“两年后正确答案的徽章!” 啊..现在我为什么不知道:) 有没有办法跳过无效标签? @james 取决于你所说的跳过是什么意思?以上是关于PHP 中的容错 HTML/XML/SGML 解析的主要内容,如果未能解决你的问题,请参考以下文章