是否可以截断给定节点上的XPath轴?

问题描述:

我一直在写一些代码,从网页中提取主要的文本内容。一个有用的策略是找到第一段内容,然后选择所有以下兄弟元素,但不包括第一个不是p,ul,ol或元素的兄弟元素。在Perl,代码看起来是这样的:是否可以截断给定节点上的XPath轴?

my ($firstpara) = $document->findnodes('//p[whatever]'); 
my @content = ($firstpara); 
for my $sibling ($firstpara->findnodes('following-sibling::*')) { 
    last if $sibling->tag !~ /^(?:p|ol|ul|blockquote)\z/; 
    push @content, $sibling; 
} 

这是不是太糟糕,但它会很酷,能够得到我想要只使用XPath的节点,所以我可以写这样的事情,而不是:

my ($firstpara) = $document->findnodes('//p[whatever]'); 
my @content = ($firstpara, $firstpara->findnodes('<query>')); 

我已经做了很多实验,但一直未能弄清楚如何编写最后一个查询。最近的一个有效的寻找的解决方案,我已经能够找到的是一样的东西:

$firstpara->findnodes('following-sibling::*[position() < $EXPR]'); 

...其中$EXPR是一些表达式返回一个同级的其标记为不pul的位置, ol或,但是我一直无法弄清楚如果这样的表达式在XPath中是可以表达的。

有什么办法可以做到我在XPath中描述的内容?

例子:

假设我的文件看起来是这样的:

<h1>Header</h1> 
<p>Paragraph 1</p> 
<p id="first">Paragraph 2</p> 
<p>Paragraph 3</p> 
<ul><li>Item 1</li><li>Item 2</li></ul> 
<p>Paragraph 4</p> 
<hr> 
<p>Paragraph 5</p> 
<blockquote>Blockquote 1</blockquote> 
... 

我有ID first一个参考<p>元素。我在XPath表达式之后,使用该元素作为内容节点,这将给我以下兄弟姐妹Paragraph 3,无序列表和Paragraph 4<hr>元素不在我想要的那些元素之中(<p>,<ul>,<ol><blockquote>),以便元素及其后的所有同级元素不应该成为返回节点集的一部分。

+0

冗长而混乱。请提供一个简单的XML作为示例,并指出您要选择哪些节点 - 解释每个节点必须满足的规则。 “ – 2012-02-10 14:12:29

+0

”以下所有的兄弟元素,但不包括第一个不是p,ul,ol或blockquote元素的兄弟元素“是冗长而混乱的? – Sean 2012-02-11 21:00:54

+0

请编辑问题 - 没有多少人会阅读评论。 – 2012-02-12 00:06:33

由于OP解释,他想:

以下所有兄弟元素最多,但不包括,这不是AP,UL,OL,或BLOCKQUOTE元素

的 第一个

I.的XPath 1.0溶液:

所希望的节点是两个节点集的交集:

  1. 被继pid的兄弟姐妹值'first'所有元素。

  2. 所有在hr之前的兄弟姐妹的元素。

要使用XPath 1.0找到此我们使用Kayessian公式节点集相交

$ns1[count(.|$ns2) = count($ns2)] 

以上的XPath表达式选择属于同时向节点集的所有节点$ns1到节点集$ns2

$vP1定义为/*/p[@id='first']

$vFirstNotInRange是:

$vP1/following-sibling::* 
    [not(self::p or self::ul 
     or self::ol or self::blockquote) 
    ] [1] 

此选择第一不需要的节点(在此情况下hr),或更精确地:那就是$vP1一个下列同属和这不是一个p第一元件,一个ulol或。

然后,我们要交两个节点集都是下面的$vP1兄弟姐妹的$vFirstNotInRange所有前面的兄弟姐妹:

让我们$vFollowingP1表示第一个节点集合 - 这就是:

$vP1/following-sibling::* 

让我们与$vPreceedingNotInRange表示第二节点集合 - 这就是:

$vFirstNotInRange/preceding-sibling::* 

最后,我们在Kayessina公式$ns1中用$vPreceedingNotInRange$ns2替换为$vFollowingP1。这些替代的reult准确选择想要的节点:

$vPreceedingNotInRange 
     [count(.|$vFollowingP1) 
     = 
      count($vFollowingP1) 
     ] 

如果我们替换所有的变量,直到我们得到一个不包含任何变量的表达式,我们得到:

/*/p[@id='first']/following-sibling::* 
    [not(self::p or self::ul 
     or self::ol or self::blockquote 
     ) 
    ] [1] 
     /preceding-sibling::* 
      [count(.| /*/p[@id='first']/following-sibling::*) 
      = 
      count(/*/p[@id='first']/following-sibling::*) 
      ] 

这种表达准确选择想要的节点。

下面是一个XSLT - 基于验证

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:variable name="vP1" select="/*/p[@id='first']"/> 

<xsl:variable name="vFirstNotInRange" select= 
    "$vP1/following-sibling::* 
    [not(self::p or self::ul 
     or self::ol or self::blockquote) 
    ] [1]"/> 

<xsl:variable name="vFollowingP1" 
     select="$vP1/following-sibling::*"/> 

<xsl:variable name="vPreceedingNotInRange" 
     select="$vFirstNotInRange/preceding-sibling::*"/> 

<xsl:template match="/"> 
    <xsl:copy-of select= 
    "$vPreceedingNotInRange 
    [count(.|$vFollowingP1) 
    = 
    count($vFollowingP1) 
    ]"/> 
================ 

    <xsl:copy-of select= 
    "/*/p[@id='first']/following-sibling::* 
    [not(self::p or self::ul 
     or self::ol or self::blockquote 
     ) 
    ] [1] 
     /preceding-sibling::* 
      [count(.| /*/p[@id='first']/following-sibling::*) 
      = 
      count(/*/p[@id='first']/following-sibling::*) 
      ] 

    "/> 
</xsl:template> 
</xsl:stylesheet> 

当施加这种转变在下面的XML文档(所提供的非简洁(wellformed)XML片段 - 校正,并且包裹在为了造简洁(wellformed)):

<html> 
    <h1>Header</h1> 
    <p>Paragraph 1</p> 
    <p id="first">Paragraph 2</p> 
    <p>Paragraph 3</p> 
    <ul> 
     <li>Item 1</li> 
     <li>Item 2</li> 
    </ul> 
    <p>Paragraph 4</p> 
    <hr/> 
    <p>Paragraph 5</p> 
    <blockquote>Blockquote 1</blockquote> 
</html> 

两个XPath表达式(一个变量和一个与取代的所有变量)是EV aluated和想要的,正确的选择的节点输出

<p>Paragraph 3</p> 
<ul> 
    <li>Item 1</li> 
    <li>Item 2</li> 
</ul> 
<p>Paragraph 4</p> 
================ 

    <p>Paragraph 3</p> 
<ul> 
    <li>Item 1</li> 
    <li>Item 2</li> 
</ul> 
<p>Paragraph 4</p> 

II。 XPath 2。0溶液

$vFirstNotInRange/preceding-sibling::* 
           [. >> $vP1] 

这将选择的$vFirstNotInRange前述任一兄弟,其也下列$vP1和选择相同的通缉节点:

<p>Paragraph 3</p> 
<ul> 
    <li>Item 1</li> 
    <li>Item 2</li> 
</ul> 
<p>Paragraph 4</p> 

说明:在这里,我们使用XPath 2.0 “跟随”运营商>>