使用 DOMDocument 加载包含命名空间的 HTML


Load HTML containing namespaces with DOMDocument

我有问题。我想加载一个带有命名空间的 HTML 代码段,其中包含 DOMDocument .

<div class="something-first">
    <div class="something-child something-good another something-great">
        <my:text value="huhu">
    </div>
</div>

但是我不知道如何保留命名空间。我尝试用loadHTML()加载它,但 HTML 没有命名空间,因此它们被剥离了。

我尝试用loadXML()加载它,但这不起作用<my:text value="huhu">因为 XML 不正确。

我需要的是一个不剥离命名空间的loadHTML()方法或一个不验证标记的loadXML()方法。所以这两种方法的组合。

到目前为止我的代码:

$html = '<div class="something-first">
    <div class="something-child something-good another something-great">
        <my:text value="huhu">
    </div>
</div>';
libxml_use_internal_errors(true);
$domDoc = new DOMDocument();
$domDoc->formatOutput = false;
$domDoc->resolveExternals = false;
$domDoc->substituteEntities = false;
$domDoc->strictErrorChecking = false;
$domDoc->validateOnParse = false;
$domDoc->loadHTML($html/*, LIBXML_NOERROR | LIBXML_NOWARNING*/);
$xpath = new DOMXPath($domDoc);
$xpath->registerNamespace ( 'my', 'http://www.example.com/' );
// -----> This results in zero nodes cause namespace gets stripped by loadHTML()
$nodes = $xpath->query('//my:*');
var_dump($nodes);

有没有办法实现我想要的?我会很高兴得到任何建议。

编辑 我打开了 libxml2 的增强请求,以提供在 HTML 中保留命名空间的选项:https://bugzilla.gnome.org/show_bug.cgi?id=711670

首先,只允许在XML(或XHTML)中使用命名空间。HTML 不支持命名空间。


假设它是 XHTML 并且代码段中存在 xmlns 声明,那么您可以使用 DOMDocument::getElementsByTagNameNS() 按命名空间访问元素:

$html = <<<EOF
<div xmlns:my="http://www.example.com/" class="something-first">
    <div class="something-child something-good another something-great">
        <my:text value="huhu" />
    </div>
</div>
EOF;
$domDoc = new DOMDocument();
$domDoc->loadXML($html);
var_dump(
  // it is possible to use wildcard `*` here
  $domDoc->getElementsByTagNameNS('http://www.example.com/', '*')
);

但是,由于命名空间声明通常是在根元素<html>而不是子节点中定义的,因此上面的代码在大多数情况下不起作用。

因此,解决方案的第二部分是检查声明是否存在,如果没有注入它......(正在研究这个)


正如我所说,上面的代码仅适用于XML/XHTML。如何使用 HTML 执行此操作仍然是开放的。(查看下面的讨论)

从技术上讲,它既不是有效的XML也不是HTML(或XHTML),因为HTML不允许命名空间元素,而有效的XML要求空元素是自闭合的并且命名空间被注册。所以你基本上问"我怎样才能让DOMDocument把这个无效的HTML视为有效的XML,即使它也不是有效的XML?"这将证明是困难的,有人可能会问为什么要更新libxml以允许这样做? 如果我将您的代码段更新为:

$html = <<<XML
<div xmlns:my="http://www.example.com/" class="something-first">
    <div class="something-child something-good another something-great">
        <my:text value="huhu" />
    </div>
</div>
XML;

添加 NS 注册并关闭my:text ,它适用于:

$domDoc = new DOMDocument();
$domDoc->loadXML($html);
echo $domDoc->saveXML();

请注意,命名空间未被剥离。据我了解,命名空间被剥离了,因为它不是有效的 XML 或 HTML。XPath 无法按命名空间进行查询,因为命名空间不是通过 xmlns 定义的,因此已被删除。

所以我想问题是:你为什么要请求无效的XML支持而不是添加右斜杠?是因为数据来自外部源,还是因为在某些上下文中空的非结束标记有效?