在PHP中检索相对DOM节点

时间:2010-11-27 14:02:30

标签: php dom xpath

我想检索文档中下一个元素标记的数据,例如:

我想仅为每个不同的范围检索<blockquote> Content 1 </blockquote>

<html>
<body>


<span id=12341></span>
<blockquote>Content 1</blockquote>
<blockquote>Content 2</blockquote>

<!-- misc html in between including other spans w/ no relative blockquotes-->

<span id=12342></span>
<blockquote>Content 1</blockquote>

<!-- misc html in between including other spans w/ no relative blockquotes-->

<span id=12343></span>
<blockquote>Content 1</blockquote>
<blockquote>Content 2</blockquote>
<blockquote>Content 3</blockquote>
<blockquote>Content 4</blockquote>

<!-- misc html in between including other spans w/ no relative blockquotes-->    

<span id=12344></span>
<blockquote>Content 1</blockquote>
<blockquote>Content 2</blockquote>
<blockquote>Content 3</blockquote>


</body>
</html>

现在我想知道两件事:

1.)如何编写一个匹配的表达式,并且只输出一个紧跟在一个封闭元素(<span></span>)之后的块引用?

2.)如果我想要,如果我仍然需要在将来输出它们同时仍然适用上一个问题的规则,我怎么能得到内容2,内容3等?

4 个答案:

答案 0 :(得分:3)

  

现在我想知道两件事:

     

1.)如何编写匹配且仅输出blockquote的表达式   关闭后立即关注   元素(<span></span>)?

假设提供的文本转换为格式良好的XML文档(您需要将id属性的值括在引号中)

使用

/*/*/span/following-sibling::*[1][self::blockquote]

这意味着用英语:选择所有 blockquote 元素,每个元素都是第一个, 跟随的兄弟{ {1}} 元素,它是文档顶部元素的祖母

  

2.)如果我想,我怎么能得到内容2,内容3等等   需要输出它们   未来虽然仍然适用于   上一个问题的规则?

即可。

您可以在span之后获取所有久负盛名的blockquote元素:

span

您可以通过获取(N + 1)-st /*/*/span/following-sibling::blockquote [preceding-sibling::*[not(self::blockquote)][1][self::span]] 之后的一组blockquote元素:

span

其中/*/*/span/following-sibling::blockquote [preceding-sibling::* [not(self::blockquote)][1] [self::span and count(preceding-sibling::span)=$vN] ] 应由数字N代替。

因此,选择了第一个$vN之后的blockquote元素集合

span

第二个/*/*/span/following-sibling::blockquote [preceding-sibling::* [not(self::blockquote)][1] [self::span and count(preceding-sibling::span)=0] ] 之后的blockquote元素集合由选择:

span

<强>等即可。 ...

XPath Visualizer中查看由以下表达式选择的节点

/*/*/span/following-sibling::blockquote
           [preceding-sibling::*
             [not(self::blockquote)][1]
                [self::span and count(preceding-sibling::span)=1]
           ]

alt text

答案 1 :(得分:0)

简答:将HTML加载到DOMDocument,然后使用XPath选择所需的节点。

http://www.php.net/DOM

答案很长:

$flag = false;
$TEXT = array();
foreach ($body->childNodes as $el) {
    if ($el->nodeName === '#text') continue;
    if ($el->nodeName === 'span') {
        $flag = true;
        continue;
    }
    if ($flag && $el->nodeName === 'blockqoute') {
        $TEXT[] = $el->firstChild->nodeValue;
        $flag = false;
        continue;
    }
}

答案 2 :(得分:0)

请尝试以下*

/html/body/span/following-sibling::*[1][self::blockquote]

匹配任何第一个blockquotes后面的span元素是body的直接子元素

//span/following-sibling::*[1][self::blockquote]

匹配文档中任意位置的任何第一个块引用

* 编辑:修复Xpath。 Dimitre的积分。我的初始版本将匹配跨度后的任何第一个blockquote,例如它会匹配span p blockquote,这不是你想要的。

以上两者都匹配“内容1”块引用。如果你想匹配跨度后的其他blockquotes(兄弟姐妹,而不是后代),请删除[1]

示例:

$dom = new DOMDocument;
$dom->load('yourFile.xml');
$xp = new DOMXPath($dom);
$query = '/html/body/span/following-sibling::*[1][self::blockquote]';
foreach($xp->query($query) as $blockquote) {
    echo $dom->saveXml($blockquote), PHP_EOL;
}

如果你想在没有XPath的情况下这样做,你可以做到

$dom = new DOMDocument;
$dom->preserveWhiteSpace = FALSE;
$dom->load('yourFile.xml');
$body = $dom->getElementsByTagName('body')->item(0);
foreach($body->getElementsByTagName('span') as $span) {
    if($span->nextSibling !== NULL &&
       $span->nextSibling->nodeName === 'blockquote')
    {
        echo $dom->saveXml($span->nextSibling), PHP_EOL;
    }
}

如果您抓取的HTML不是有效的XHTML,请使用loadHtmlFile()来加载标记。您可以使用libxml_use_internal_errors(TRUE)libxml_clear_errors()来抑制错误。

另请参阅Best methods to parse HTML了解DOM的替代方案(尽管我认为DOM是一个不错的选择)。

答案 3 :(得分:0)

除了@Dimitre good answer之外,您还可以使用:

/html
   /body
      /blockquote[preceding-sibling::*[not(self::blockquote)][1]
                     /self::span[@id='12341']]