xpath 通过包含值的父属性排除元素及其所有子元素

Posted

技术标签:

【中文标题】xpath 通过包含值的父属性排除元素及其所有子元素【英文标题】:xpath exclude element and all its children by parent attribute containing a value 【发布时间】:2015-04-28 14:13:26 【问题描述】:

标记示例:

<div class="post-content">
    <p>
        <moredepth>
            <...>
                <span class="image-container float_right">
                    <div class="some_element">
                        image1
                    </div>
                    <p>do not need this</p>
                </span>
                <div class="image-container float_right">
                    image2
                </div>
                <p>text1</p>
                <li>text2</li>
            </...>
        </moredepth>
    </p>
</div>

最糟糕的是“图像容器”的深度可以在任何级别。

我尝试使用的Xpath:

//div[contains(@class, 'post-content')]//*[not(contains(@class, 'image-container'))]

我应该使用什么 Xpath 才能排除“some_element”和任何深度的“image-container”的任何其他子级以及“image-container”元素本身?

本例中的输出应为:

<p>
    <moredepth>
        <...>

            <p>text1</p>
            <li>text2</li>
        </...>
    </moredepth>
</p>

附:是否可以使用 CSS 进行这样的选择?

【问题讨论】:

请编辑您的帖子并包含您期望的输出(发布此 html)。 为什么不能选择外部的pmoredepth 元素?你只想要moredepth 的孩子吗? 我需要选择 p 和更多深度。但我需要排除包含“图像容器”及其所有子元素的类 @MathiasMüller 再次编辑了输出标记的问题 【参考方案1】:

您可以应用 Kaysian 方法来获得集合的交集。你有两套:

A:从//div[contains(@class, 'post-content')] 下降的元素,不包括当前元素(因为你不想要根div):

//*[ancestor::div[contains(@class, 'post-content')]]

B:来自//*[not(contains(@class, 'image-container'))]的元素,包括当前元素(因为要排除整个树,包括divspan):

//*[not(ancestor-or-self::*[contains(@class, 'image-container')])] 

这两组的交集是解决您问题的方法。 Kaysian 方法的公式为:A [ count(. | B) = count(B) ]。将其应用于您的问题,您需要的结果是:

//*[ancestor::div[contains(@class, 'post-content')]]
   [ count(. | //*[not(ancestor-or-self::*[contains(@class, 'image-container')])])
     = 
     count(//*[not(ancestor-or-self::*[contains(@class, 'image-container')])]) ]

这将从您的示例代码中选择以下元素:

/div/p
/div/p/moredepth
/div/p/moredepth/...
/div/p/moredepth/.../p
/div/p/moredepth/.../li

排除与不需要的类及其后代匹配的spandiv

然后,您可以向表达式添加额外的步骤,以准确过滤掉您需要的文本或节点。

【讨论】:

太棒了! not(ancestor-or-self::*) 的事情奏效了。万一有人需要它,我需要类 link-list 中没有 aside 祖先的所有 ul 元素,所以我写了 "//ul[contains(@class, 'link-list') and not(ancestor-or-self::aside)]"【参考方案2】:

一旦 XML 片段通过路径表达式返回给您,XPath 就不允许对其进行操作。所以,你不能选择moredepth

//moredepth

没有得到这个元素节点的all结果,包括你想排除的所有后代节点:

<moredepth>
<span class="image-container float_right">
<div class="some_element">
image1
</div>
<p>do not need this</p>
</span>
<div class="image-container float_right">
image2
</div>
<p>text1</p>
<li>text2</li>
</moredepth>

你可以只选择moredepth的子节点:

//div[contains(@class, 'post-content')]/p/moredepth/*[not(contains(@class,'image-container'))]

将产生(由-------分隔的单个结果):

<p>text1</p>
-----------------------
<li>text2</li>

【讨论】:

虽然,这个答案并没有解决我的问题,但它是正确的。您不能用 XPath 做到这一点。我最终为这个任务编写了我的自定义脚本。

以上是关于xpath 通过包含值的父属性排除元素及其所有子元素的主要内容,如果未能解决你的问题,请参考以下文章

具有特定值的任何属性的所有元素的 XPath?

Xpath:选择除具有特定属性的元素之外的所有元素

css 或 xpath 选择器:具有特定值的任何属性的元素

xpath 选择按属性值列表排除的节点

Python爬虫lxml解析实战

XPath语法