【问题标题】:PHP DOM Parser - get all nodes between two known XPathsPHP DOM Parser - 获取两个已知 XPath 之间的所有节点
【发布时间】:2023-03-31 02:15:01
【问题描述】:

如何选择 2 个已知 XPath 之间的所有 DOM 节点?

Xpath1 = html/body/div[2]/p
Xpath2 = html/body/div[2]/p/a[3]

DOM 结构:

<html>
<body> 
<div id="id3">
    <p id="p3">
        text1 
        <a href="#">
            goal
        </a>
        text2 
        <a href="#">
            Crowdrise
        </a>.
    </p>
</div>
</body>
</html>

解析器:

$dom = new DOMDocument();
$dom->loadHTML($domain);

$x = new DOMXPath($dom); 
$el = $x->query("....??");

所以,基本上是在寻找一种查询方法来选择两个 XPath 之间的所有节点。 我看到了几个类似的问题,但它们似乎与 XSLT 案例有关。

【问题讨论】:

  • between 是什么意思?如果你在一棵树上选择 2 个树枝,哪些树枝是between?此外,XSLT 使用 XPath 作为其表达式语言,因此这些答案可能适用。
  • XPath 中没有分支。一切都是一个节点。如果您有 20 个节点并选择 7 和 15,则需要一种方法来选择节点 7-15(包括)
  • XPath 是一种树寻址语言,树的概念就是分支的概念。您将如何订购这些节点 1..20,确切地说?这就是我的意思:如果我采用“中间的所有节点”,我必须知道从一个节点到另一个节点的路径。从最深的节点开始,我会上升一步(父节点),然后环顾四周,看看另一个节点是否是兄弟节点。但是,如果必须的话,你不会在任何树中的任何两个节点之间找到一条这样的路径,而不需要递归(下降到兄弟节点)。
  • 在您的示例中,Xpath1Xpath2 之间没有节点。后者是前者的直系子代。或者,如果包含开头和结尾,则路径 Xpath1, Xpath2.
  • 让我更困惑的是,发布的 HTML 与 xpath 示例不对应,即 HTML 中的任何地方都没有 div[2]a[3]

标签: php html dom xpath domparser


【解决方案1】:

好问题。 没有通用的方法可以做到这一点,因为它取决于第二个元素相对于第一个元素的位置。我的意思是,如果第二个元素是第一个元素的后代或者它在另一个分支中 - 这两个是完全不同的。 所以我们需要做一个假设:

  • 假设第二个路径定义的第二个元素始终是第一个路径定义的第一个元素的后代。

我们的目标是获取第一个元素的所有后代元素(不包括文本节点),而不是第二个元素的共享后代。

要实现这一点,我们需要一个表达式:

el1 = All element 1 descendants.
el2 = All element 2 descendants including self. 
result = el1 [position() <= count( el1 ) - count( el2 )]

如您所见,我们正在构建一组前 N 个元素,直到我们到达第二个元素。

这是一个例子:

<?php

$dom = new DOMDocument();
$dom->loadHTML('<html>'
    . '         <body>'
    . '             <div>'
    . '                 <h1>shlomi</h1>'
    . '                 <p>'
    . '                     <span>goal1</span>'
    . '                     text1' 
    . '                     <a href="#">goal2</a>'
    . '                     text2'
    . '                     <a href="#"><span></span>Crowdrise</a>'
    . '                     .' 
    . '                 </p>'
    . '             </div>'
    . '         </body>'
    . '     </html>');

$x = new DOMXPath($dom); 

$path1 = "/html/body/div/p/descendant::*";               // all descendant elements without text
$path2 = "/html/body/div/p/a[2]/descendant-or-self::*";  // all descendant elements without text including self
$path3 = $path1."[position() <= count(".$path1.") - count(".$path2.")]"; 
$elList = $x->query($path3);

foreach ($elList as $node) {
      echo $node->nodeName." -> text: ".$node->textContent."<br />";
}

这将打印:

span -> text: goal1
a    -> text: goal2

注意我使用 * 仅定位没有文本节点的元素 - 如果您希望所有节点都将其替换为 node()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-24
    • 1970-01-01
    • 2019-04-19
    • 1970-01-01
    • 2011-01-24
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    相关资源
    最近更新 更多