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

5

我有一堆旧的文档,它们看起来像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 的 span)。
是否存在更优雅的解决方案? 有没有方法让 DOMDocument 知道要考虑哪些其他标签作为有效标签? PHP 中是否有不同的强大 HTML 解析类/对象?
(如果不明显的话,我认为在这里正则表达式不是一个有效的解决方案)
更新:伪标签中的信息是目标之一,因此 Tidy 等工具不可行。 另外,我想找到一个能够为我执行某些(如果不是全部)规范性清理的工具,这就是我首先查看 DomDocument 的 loadHTML 方法的原因。
6个回答

7
您可以在加载文档时使用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 函数。这个函数可以帮助你获取到相关的警告信息。

3
你应该等几周,这样你就可以获得“两年后正确答案徽章”了! - Alana Storm
啊哈..现在我为什么不知道呢 :) - troelskn
有没有办法跳过无效的标签? - James P.
@james 取决于你所说的“跳过”是什么意思? - troelskn

2

我想知道通过HTML Tidy清理“不良”HTML是否有助于第一步?如果您可以使文档格式合法,也许您可以将其作为常规XML文件加载到DomDocument中进行处理。


抱歉,我应该更具体地说明,我需要从文件中解析出的部分是在虚假标签中找到的内容。 - Alana Storm
我建议使用HTMLTidy作为预处理步骤,尝试获取格式良好的XML文档,然后您可以使用DomDocument解析它并读取整个DOM。祝您好运 :) - Paul Dixon
整理功能是否可以清除伪标记以及它所做的所有重新格式化呢? - Alana Storm

1

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

一旦您使文件格式正确,那么您就可以开始查看XML解析器,否则您将无法解决问题。Lok Alejo说,您可以查看HTML TIDY,但它似乎只适用于HTML,我不知道它如何处理您的自定义元素。

我不认为正则表达式是一个有效的解决方案

在您获得格式正确之前,这可能是您唯一的选择。一旦您将文档转换到该阶段,然后您就可以使用DOM函数了。


当您使用DOMDocument加载HTML文件时,它似乎会进行某种程度的清理以确保格式正确,但需要所有标签都是合法的HTML标签。我正在寻找一些只做前者而不是后者的东西。 - Alana Storm

1

0

@Alan Storm

你在我之前的回答中的留言让我开始思考:

当您使用DOMDocument加载HTML文件时,它似乎会清理一些关于格式良好的问题,但需要所有标签都是合法的HTML标签。我正在寻找做前者而不是后者的东西。(Alan Storm)

对标记运行正则表达式(抱歉!),当它发现一个不是有效HTML元素的元素时,用一个您知道不存在于任何文档中的有效元素替换它,并给它一个带有非法元素名称的属性值,以便您可以随后将其切换回来。例如:

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

很明显,那段代码不会起作用,但你大概懂意思吧?

0

我解决这个问题的方法是运行一个循环,将我的自定义标签列表与正则表达式匹配。 正则表达式无法捕获其中有另一个内部自定义标签的标签。

当匹配成功时,会调用处理该标记的函数并返回“已处理的HTML”。 如果该自定义标记位于另一个自定义标记内部,则通过实际插入子元素的位置使其父级成为无子级,并且在下一次循环迭代中将被正则表达式匹配并处理。

只要没有无子级的自定义标记要匹配,循环就会结束。 总体而言,它是迭代的(使用while循环),而非递归的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接