使用XPath选择两个元素之间的所有节点,从交集中排除不必要的元素



文档结构如下:

<div class="document">
    <div class="title">
        <AAA/>
    </div class="title">
    <div class="lead">
        <BBB/>
    </div class="lead">
    <div class="photo">
        <CCC/>
    </div class="photo"> 
    <div class="text">
    <!-- tags in text sections can vary. they can be `div` or `p` or anything. -->
        <DDD>
            <EEE/>
            <DDD/>
            <CCC/>
            <FFF/>
                <FFF>
                    <GGG/>
                </FFF>
        </DDD>
    </div class="text">
    <div class="more_text">
        <DDD>
        <EEE/>
            <DDD/>
            <CCC/>
            <FFF/>
                <FFF>
                    <GGG/>
                </FFF>
        </DDD>
    </div class="more_text">
    <div class="other_stuff">
        <DDD/>
    </div class="other_stuff">
</div class="document">

任务是抓取<div class="lead"><div class="other_stuff">之间的所有元素,除了<div class="photo">元素。

节点集相交$ns1[count(.|$ns2) = count($ns2)]的Kayessian方法效果很好。将$ns1替换为//*[@class="lead"]/following::*,将$ns2替换为//*[@class="other_stuff"]/preceding::*,工作代码如下所示:

//*[@class="lead"]/following::*[count(. | //*[@class="other_stuff"]/preceding::*)
= count(//*[@class="other_stuff"]/preceding::*)]/text()

它选择<div class="lead">和<div class="other_stuff"> 之间的所有内容,包括 <div class="photo">元素。我尝试了几种方法来插入not()选择器在公式本身

//*[@class="lead" and not(@class="photo ")]/following::*
//*[@class="lead"]/following::*[not(@class="photo ")]
//*[@class="lead"]/following::*[not(self::class="photo ")]

(与/preceding::*部分相同),但它们不起作用。看起来这个not()方法被忽略了——<div class="photo">元素仍然在选择中。

问题1:如何从这个交集中排除不必要的元素?

这不是一个选项,从<div class="photo">元素自动排除它,因为在其他文档中,它可以出现在任何位置或根本不出现。

问题2(附加):在这种情况下,following::preceding::之后使用*是否可以?

它最初选择整个文档直到末尾和开头的所有内容。为following::preceding::方式指定确切的终点是否更好?我试过//*[@class="lead"]/following::[@class="other_stuff"],但它似乎不起作用。

问题1:如何从这个交集中排除不必要的元素?

将另一个谓词(在本例中是[not(self::div[@class='photo'])])添加到您的工作XPath中就可以了。对于这种特殊情况,整个XPath看起来像这样(为了可读性进行了格式化):

//*[@class="lead"]
 /following::*[
    count(. | //*[@class="other_stuff"]/preceding::*) 
        = 
    count(//*[@class="other_stuff"]/preceding::*)
 ][not(self::div[@class='photo'])]
/text()

问题2(附加):在这种情况下,在后面的::和前面的::之后使用*是否可以?

我不确定它是否会"更好",我能告诉的是following::[@class="other_stuff"]是无效的表达式。您需要提到将应用谓词的元素,例如,'any element' following::*[@class="other_stuff"],或者只是'div' following::div[@class="other_stuff"]

最新更新