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

xpath exclude element and all its children by parent attribute containing a value

标记示例:

<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>

最糟糕的是 "image-container" 的深度可以是任何级别。

我尝试使用的 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>

P.S。是否可以使用 CSS 进行这样的选择?

XPath 不允许在路径表达式返回给您后对 XML 的片段进行操作。所以,你不能 select moredepth:

//moredepth

没有得到此元素节点的所有所有,包括您要排除的所有后代节点:

<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>

你能做的只是selectmoredepth的子节点:

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

这将产生(个别结果由 ------- 分隔):

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

你可以应用凯斯方法求集合的交集。你有两组:

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')])] 

这两组的交集就是您问题的解决方案。凯氏方法的公式为: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')])]) ]

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

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

排除匹配不需要的 class 的 spandiv 及其后代。

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