【问题标题】:Load HTML containing namespaces with DOMDocument使用 DOMDocument 加载包含命名空间的 HTML
【发布时间】:2013-11-20 06:46:33
【问题描述】:

我有问题。我想用DOMDocument 加载一个带有命名空间的HTML sn-p。

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

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

我尝试使用 loadXML() 加载它,但这也不起作用,因为 &lt;my:text value="huhu"&gt; 不是正确的 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

【问题讨论】:

  • 在使用loadXMLloadHTML 时,加载既不是有效的XML 也不是有效的HTML 的东西总是很棘手...
  • 可以声明命名空间吗?像&lt;my:root_node xmlns:my="http://www.w3.org/TR/html4/"&gt;…&lt;my:text&gt;… 这样的东西。 DOMDocument 在通过 loadXML() 或 load() 加载时应该能够处理命名空间。
  • 已删除我的答案,因为它不符合您的需求。但也许它 - 可悲但真实 - 根本不起作用。绝对是一个有趣的问题.. +1
  • @jazZRo 不,它不会起作用,因为&lt;my:text value="huhu"&gt; 不是有效的 XML :-(.
  • @jazZRo 是的,这也是我在问我的问题。但是当只解析 HTML 的 sn-ps 时,例如 &lt;div&gt;,那么命名空间声明在该 sn 中不可用是很常见的-p

标签: php html xml dom xpath


【解决方案1】:

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


鉴于它是 XHTML 并且 xmlns 声明存在于 sn-p 中,那么您可以使用 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/', '*')
);

但是,由于命名空间声明通常在根元素 &lt;html&gt; 中而不是在子节点中定义,所以上面的代码在大多数情况下都不起作用..

所以解决方案的第二部分是检查声明是否存在,如果不注入它......(正在处理这个问题)


正如我所说,上面的代码仅适用于 XML / XHTML。它仍然是开放的如何用 HTML 做到这一点。 (查看下面的讨论)

【讨论】:

  • 这不起作用,因为在使用loadHTML() 解析我的 HTML sn-p 期间命名空间被剥离。
  • 是的,你是对的。您只能选择 text 节点..(似乎是这样,让我深入研究一下)
  • 我想访问命名空间 my 的所有元素。因此,不幸的是,使用//text 访问元素也不是一个选项:-(。如果你能找到一种方法来实现我不想要的东西,那就太好了:-)。
  • 我正在寻找方法
  • 到目前为止一切顺利。我今天早些时候也有。问题是,您必须输入有效的 XML。因此,如果您的 sn-p 缺少关闭 &lt;/div&gt; 或类似的东西。 loadXML() 将失败。
【解决方案2】:

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

$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 支持而不是添加结束斜线?是因为数据来自外部来源,还是因为在某些情况下空的非结束标签有效?

【讨论】:

  • 很高兴在这里看到另一个意见.. 不幸的是它和我的一样......(你应该怎么说,我想就像你和我说的那样。).. 然而,@ 987654324@ 元素很痛苦!你真的认为 facebook 编写了无效的 HTML ....(只是一个问题)?...也许我们应该问他们...
  • 可能是您没有看到使用loadHTMLloadXML 时的行为有所不同(就像我以前一样)...我认为这是一个合理的问题,因为它是一个真实的问题世界问题..(OP 没有设计 HTML。它可以是任何东西)
  • 我的猜测是 Facebook 提供有效的 XHTML,但我不能肯定地说,因为我从不与 Facebook 交互。如果为fb 命名空间提供了xmlns,那么它是有效的。 html 格式错误是一回事,但 XML 通常被更严格地解析,并且对于命名空间,除了最佳实践之外,还需要拥有 xmlns 。 Chrome 不会显示原始的 sn-p,为什么要使用不那么宽容的词法分析器?
  • @hek2mgl - 忽略最后一条评论。触摸屏上的胖手指。我发现真正有趣的是关闭空元素的持续阻力。可能有 100 多个问题与不执行此规则的目标相关,更不用说 HTML 规范中关于是否执行此规则的大量反复讨论,但对我来说,如果您有一个标签可以解释为开始标签但没有结束标签(如&lt;br&gt;)它应该有一些礼貌的指示符(如&lt;br/&gt;)通知解析器没有结束标签。
  • 是的。我无法理解这个讨论 (&lt;br&gt; or &lt;br/&gt;).. 这是&lt;br/&gt;.. 就是这样! ! :) 但是我们需要讨论这个&lt;fb:*&gt; 元素(如果你喜欢,当然).. 因为它们不只是由 facebook 提供的。它们包含在几个(数百万?)其他(HTML)网站中。我今天累了。但真的很想在这里找到最终答案。 (出于这个原因,我什至会与魔鬼打交道并创建一个 fb 帐户(如有必要)):)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-20
  • 1970-01-01
  • 2012-10-24
  • 2011-01-02
  • 1970-01-01
  • 1970-01-01
  • 2011-03-01
相关资源
最近更新 更多