PHP中的容错HTML / XML / SGML解析

PHP中的容错HTML / XML / SGML解析

Error Tolerant HTML/XML/SGML parsing in PHP

我有一堆类似HTML的旧文档。就像它们一样,它们看起来像HTML,但是有一些附加的组成标记,这些标记不是HTML的一部分

1
This is an example of a <pseud-template>fake tag</pseud-template>

我需要解析这些文件。 PHP是唯一可用的工具。这些文档并不能很好地构成XML。

我最初的想法是在PHP DOMDocument上使用loadHTML方法。但是,这些方法使组成HTML标记阻塞,并且将拒绝解析字符串/文件。

1
2
3
4
$oDom = new DomDocument();
$oDom->loadHTML("This is an example of a <pseud-template>fake tag</pseud-template>");
//gives us
DOMDocument::loadHTML() [function.loadHTML]: Tag pseud-template invalid in Entity, line: 1 occured in ....

我唯一能想到的解决方案是使用字符串替换功能对文件进行预处理,该功能将删除无效标签,并使用有效的HTML标签(可能是带有标签名称ID的跨度)替换它们。

有没有更优雅的解决方案?一种让DOMDocument知道其他标签视为有效的方法?是否有用于PHP的不同的,健壮的HTML解析类/对象?

(如果不是很明显,我认为这里的正则表达式不是有效的解决方案)

更新:假标签中的信息是此处目标的一部分,因此诸如Tidy之类的选项不可行。另外,我正在寻找对我进行某种程度(甚至不是全部)的良好格式清理的东西,这就是为什么我首先查看DomDocument的loadHTML方法的原因。


加载文档时,可以使用libxml_use_internal_errors禁止显示警告。例如。:

1
2
3
4
libxml_use_internal_errors(true);
$doc = new DomDocument();
$doc->loadHTML("This is an example of a <pseud-template>fake tag</pseud-template>");
libxml_use_internal_errors(false);

如果由于某种原因需要访问警告,请使用libxml_get_errors


我想知道通过Tidy传递"不好的" HTML是否可以作为第一遍传递?可能值得一看,如果您可以使文档格式正确,则可以使用DomDocument将其作为常规XML文件加载。


看一下PHP Fit端口中的解析器。该代码是干净的,最初旨在加载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


T
您不需要DOMDocument的DTD来解析自定义XML。只需使用DOMDocument->load(),只要XML格式正确,它就可以读取它。

一旦文件格式正确,您就可以开始查看XML解析器,然后再进行S.O.L。 Lok Alejo说过,您可以看一下HTML TIDY,但这看起来似乎是特定于HTML的,我不知道它如何与您的自定义元素一起使用。

I don't consider regular expressions a valid solution here

除非您格式正确,否则这可能是您唯一的选择。一旦文档到达该阶段,就可以使用DOM函数了。


@艾伦·斯托姆

您对其他答案的评论使我开始思考:

When you load an HTML file with DOMDocument, it appears to do some level of cleanup re: well well-formedness, BUT requires all your tags to be legit HTML tags. I'm looking for something that does the former, but not the later. (Alan Storm)

对标记运行一个正则表达式(对不起!),当它找到一个不是有效HTML元素的正则表达式时,请将其替换为您知道在任何文档中都不存在的有效元素(想到是blink ...),并为它提供带有非法元素名称的属性值,以便以后可以将其切换回去。例如:

1
2
3
$code = str_replace("<pseudo-tag>","<blink rel=\"pseudo-tag\">", $code);
// and then back again...
$code = preg_replace('<blink rel="(.*?)">', '<\\1>', $code);

显然,该代码将无法正常工作,但是您了解了总体思路吗?


我对此问题的快速而肮脏的解决方案是运行一个循环,该循环将我的自定义标签列表与正则表达式匹配。 regexp不会捕获其中包含另一个内部自定义标签的标签。

匹配时,将调用用于处理该标签的函数,并返回"已处理的HTML"。如果该自定义标签位于另一个自定义标签内,则父标签将变为无子标签,因为实际上是在子标签的位置插入了HTML,它将由regexp匹配并在循环的下一次迭代中进行处理。

当没有要匹配的无子级自定义标签时,循环结束。总体而言,它是迭代的(while循环),而不是递归的。


推荐阅读