XPath的not()函数怎么否定表达式?

幻夢星雲
发布: 2025-08-16 16:55:01
原创
628人浏览过
not()函数用于反转XPath表达式的布尔结果,常用于筛选不满足特定条件的节点。其基本形式为not(expression),可否定属性存在、属性值、文本内容或子元素存在性。常见用法包括//div[not(@class)]选择无class属性的div,//a[not(@target='_blank')]排除target为_blank的链接。误区包括混淆not()作用范围,如not(//div[@class='active'])返回布尔值而非节点集,正确写法应为//div[not(@class='active')]。not()可与and、or结合使用,遵循德摩根定律,如//div[not(@id) and not(@class)]等价于//div[not(@id or @class)]。性能上,not()本身开销小,但内部复杂表达式可能影响效率,建议避免全局搜索,优先在谓词内使用以提高效率。

xpath的not()函数怎么否定表达式?

XPath的

not()
登录后复制
函数,说白了,就是用来否定一个表达式的结果。它会把一个原本为真的条件变成假,把假的变成真。你把它想象成一个“非”门,输入是真,输出就是假;输入是假,输出就是真。这在我们需要选择那些“不符合”某个条件的元素时,简直是神器。

解决方案

要否定一个XPath表达式,你只需要将该表达式作为

not()
登录后复制
函数的参数。它的基本形式是
not(expression)
登录后复制
。这个
expression
登录后复制
可以是任何能产生布尔值(真或假)的XPath表达式。

举几个例子,你就能立刻明白它的威力了:

<ul> <li> 否定一个简单的布尔值:
not(true())
登录后复制
结果是
false
登录后复制
not(false())
登录后复制
结果是
true
登录后复制
。这看起来有点傻,但理解其核心逻辑很重要。 <li> 否定元素的存在性: 比如你想找那些没有
class
登录后复制
属性的
div
登录后复制
元素,你可以写成
//div[not(@class)]
登录后复制
。这里,
@class
登录后复制
如果存在,在布尔上下文中就是真,
not()
登录后复制
一否定就变假,所以这个
div
登录后复制
不会被选中。如果
@class
登录后复制
不存在,就是假,
not()
登录后复制
一否定就变真,这个
div
登录后复制
就会被选中。 <li> 否定属性值: 如果你想找所有
<a>
登录后复制
标签,但排除掉那些
target
登录后复制
属性是
_blank
登录后复制
的,你可以用
//a[not(@target='_blank')]
登录后复制
。这会选中所有
target
登录后复制
属性不等于
_blank
登录后复制
<a>
登录后复制
,包括那些根本没有
target
登录后复制
属性的
<a>
登录后复制
。 <li> 否定文本内容: 假设你有一堆
p
登录后复制
标签,你想找出那些不包含“重要”这个词的段落,就可以写
//p[not(contains(text(), '重要'))]
登录后复制
。 <li> 否定子元素的存在: 如果你想找那些没有
span
登录后复制
子元素的
div
登录后复制
,可以写
//div[not(./span)]
登录后复制
。注意这里的
./span
登录后复制
,它表示当前
div
登录后复制
的直接子元素
span
登录后复制

not()
登录后复制
函数非常灵活,它可以嵌套在更复杂的表达式中,或者与其他逻辑运算符(如
and
登录后复制
or
登录后复制
)结合使用,来实现更精细的筛选。

XPath中not()函数的基本用法与常见误区是什么?

说实话,

not()
登录后复制
函数用起来简单,但要真正理解它的“哲学”,避免踩坑,还是得掰扯掰扯。

基本用法:

正如前面提到的,

not()
登录后复制
的核心就是“反转”逻辑。它最常见的应用场景就是作为谓词(
[]
登录后复制
)的一部分,用来过滤节点集。

<ul><li> 过滤不符合特定条件的节点:<ul> <li>
//button[not(@disabled)]
登录后复制
:选择所有未被禁用的按钮。 <li>
//input[not(starts-with(@id, 'temp'))]
登录后复制
:选择所有ID不以“temp”开头的输入框。 <li>
//li[not(./a)]
登录后复制
:选择所有没有
<a>
登录后复制
子元素
的列表项。

常见误区:

这里有些坑,我个人就掉过好几次,后来才慢慢琢磨明白:

    <li>

    误区一:对空节点集的理解。 当一个XPath表达式的结果是空节点集时,在布尔上下文中它会被转换为

    false
    登录后复制
    。非空节点集则转换为
    true
    登录后复制

    <ul> <li>比如
    //div[not(./span)]
    登录后复制
    :如果当前
    div
    登录后复制
    下有
    span
    登录后复制
    ./span
    登录后复制
    就是非空节点集,转换为
    true
    登录后复制
    not(true)
    登录后复制
    就是
    false
    登录后复制
    ,这个
    div
    登录后复制
    不被选中。如果当前
    div
    登录后复制
    下没有
    span
    登录后复制
    ./span
    登录后复制
    就是空节点集,转换为
    false
    登录后复制
    not(false)
    登录后复制
    就是
    true
    登录后复制
    ,这个
    div
    登录后复制
    被选中。这符合预期。 <li>但如果你写
    not(//div[@class='active'])
    登录后复制
    ,这通常不是你想要的效果。这个表达式会检查整个文档中是否存在任何一个
    class
    登录后复制
    active
    登录后复制
    div
    登录后复制
    。如果存在,整个表达式就是
    false
    登录后复制
    。如果一个都没有,整个表达式就是
    true
    登录后复制
    。它返回的是一个单一的布尔值,而不是一个节点集。你大概率是想选择那些
    class
    登录后复制
    不是
    active
    登录后复制
    div
    登录后复制
    ,那应该是
    //div[not(@class='active')]
    登录后复制
    。 <li>

    误区二:

    not()
    登录后复制
    的位置和作用域
    not()
    登录后复制
    放在谓词内部,是针对当前节点进行判断;放在整个表达式外面,是针对整个表达式的结果进行判断。

    <ul> <li>
    //div[not(@class='active')]
    登录后复制
    :选择所有
    class
    登录后复制
    属性不等于
    active
    登录后复制
    div
    登录后复制
    。 <li>
    not(//div[@class='active'])
    登录后复制
    :判断文档中是否存在
    class
    登录后复制
    active
    登录后复制
    div
    登录后复制
    ,并返回其相反的布尔值。这是个全局性的判断。 <li>

    误区三:与

    and
    登录后复制
    /
    or
    登录后复制
    的结合(德摩根定律)。
    有时候我们想否定一个复合条件,比如“既不是A也不是B”。

    <ul> <li>
    not(conditionA and conditionB)
    登录后复制
    等价于
    not(conditionA) or not(conditionB)
    登录后复制
    。 <li>
    not(conditionA or conditionB)
    登录后复制
    等价于
    not(conditionA) and not(conditionB)
    登录后复制
    。 理解这一点能帮助你写出更清晰、更符合逻辑的XPath。例如,你想找既没有
    id
    登录后复制
    属性也没有
    class
    登录后复制
    属性的
    div
    登录后复制
    //div[not(@id) and not(@class)]
    登录后复制
    或者等价的
    //div[not(@id or @class)]
    登录后复制
    。我个人更倾向于前者,感觉逻辑更直观一些。

如何利用not()函数选择不包含特定属性或子元素的节点?

这几乎是

not()
登录后复制
函数最经典的用途了,也是日常工作中非常高频的场景。

Browse AI
Browse AI

AI驱动的网页内容抓取和数据采集工具

Browse AI <span>105
<span>查看详情 Browse AI

选择不包含特定属性的节点:

<ul> <li>

没有某个特定属性的节点: 如果你想找到页面上所有没有

id
登录后复制
属性的
div
登录后复制
元素,可以这样写:
//div[not(@id)]
登录后复制
这里的
@id
登录后复制
如果存在,就会被视为真,
not()
登录后复制
一否定就变假,该
div
登录后复制
不被选中。如果
@id
登录后复制
不存在,就是假,
not()
登录后复制
一否定就变真,该
div
登录后复制
就被选中。这非常简洁高效。

<li>

某个属性的值不符合预期: 假设你有一堆链接,但你只想选择那些

href
登录后复制
属性不以
#
登录后复制
开头的(排除锚点链接):
//a[not(starts-with(@href, '#'))]
登录后复制
或者你想找所有
img
登录后复制
标签,但排除掉那些
alt
登录后复制
属性为空字符串的(通常意味着图片描述缺失):
//img[not(@alt='')]
登录后复制

选择不包含特定子元素的节点:

<ul> <li>

没有某个直接子元素的节点: 你可能想找到所有没有

<span>
登录后复制
子元素的
<div>
登录后复制
//div[not(./span)]
登录后复制
这里的
./span
登录后复制
表示查找当前
div
登录后复制
的直接子元素
span
登录后复制
。如果找到了,
not()
登录后复制
就让当前
div
登录后复制
不被选中;如果没找到,就选中它。

<li>

没有某个特定类型的后代元素的节点: 如果你想找到所有

<ul>
登录后复制
列表,但排除掉那些内部任何地方(不一定是直接子元素)包含
class
登录后复制
selected
登录后复制
<li>
登录后复制
<ul>
登录后复制
//ul[not(.//li[@class='selected'])]
登录后复制
这里的
.//li
登录后复制
表示查找当前
ul
登录后复制
下的任意层级的
li
登录后复制
元素。

<li>

不包含特定文本内容的节点: 比如你有一堆

div
登录后复制
,你想找到那些内部文本不包含“广告”字样的
div
登录后复制
//div[not(contains(text(), '广告'))]
登录后复制
或者更精确一点,不包含“免费”或“优惠”:
//div[not(contains(text(), '免费') or contains(text(), '优惠'))]
登录后复制

这些都是非常实用的场景,

not()
登录后复制
函数在这里发挥了它筛选“反向条件”的巨大作用。

not()函数在复杂XPath表达式中的应用场景与性能考量

在更复杂的XPath表达式里,

not()
登录后复制
函数就像一块乐高积木,可以灵活地嵌入,实现非常精细的筛选逻辑。但同时,我们也得稍微琢磨下它的性能,虽然在绝大多数Web抓取场景下,这点性能差异可能微不足道。

复杂应用场景:

    <li>

    组合否定条件: 我们经常需要同时满足多个否定条件。比如,我想找一个

    div
    登录后复制
    ,它既没有
    id
    登录后复制
    属性,
    class
    登录后复制
    也不是
    hidden
    登录后复制
    //div[not(@id) and not(@class='hidden')]
    登录后复制
    或者,你也可以利用德摩根定律,写成:
    //div[not(@id or @class='hidden')]
    登录后复制
    这两种写法在逻辑上是等价的,具体用哪个,看你个人觉得哪个更清晰。我个人偏好第一个,感觉更直观。

    <li>

    否定函数结果:

    not()
    登录后复制
    可以否定其他函数的返回结果。

    <ul> <li>找那些文本内容不为空
    p
    登录后复制
    标签:
    //p[not(normalize-space(text()) = '')]
    登录后复制
    这里
    normalize-space()
    登录后复制
    会移除文本前后的空白字符,并将内部连续空白替换为单个空格。
    not()
    登录后复制
    再判断结果是否为空。 <li>找那些子元素数量不等于5的
    ul
    登录后复制
    //ul[not(count(./li) = 5)]
    登录后复制
    这等价于
    //ul[count(./li) != 5]
    登录后复制
    ,但用
    not()
    登录后复制
    有时候能让逻辑更统一。 <li>

    结合位置谓词: 比如,选择一个

    ul
    登录后复制
    中,除了第一个
    li
    登录后复制
    之外的所有
    li
    登录后复制
    //ul/li[not(position() = 1)]
    登录后复制
    这等价于
    //ul/li[position() > 1]
    登录后复制
    //ul/li[not(self::li[1])]
    登录后复制

性能考量:

说实话,谈性能在XPath层面,很多时候有点“玄学”,因为实际的解析器实现、文档大小、以及你所使用的库或工具的优化程度都会有影响。但总的原则是,让你的表达式越具体越好,减少引擎的遍历范围。

<ul> <li>
not()
登录后复制
本身通常不会成为性能瓶颈。
它只是一个逻辑操作符。真正的性能开销通常来自于它内部的表达式,尤其是当这个表达式需要遍历大量节点或者执行复杂计算时。 <li> 避免不必要的全局搜索: 如果你写
not(//div[@class='active'])
登录后复制
,XPath引擎可能需要遍历整个文档来确定是否存在这样的
div
登录后复制
。如果你的目标是过滤一个已知的节点集,把
not()
登录后复制
放在谓词里通常更高效。 例如,你已经定位到了一组
div
登录后复制
,现在想从这组
div
登录后复制
中筛选出没有特定属性的,那么
//div[not(@attribute)]
登录后复制
肯定比先获取所有
div
登录后复制
再在外部做
not()
登录后复制
判断要好。 <li> 复杂表达式的优化:
not()
登录后复制
内部的表达式非常复杂时,性能可能会受影响。比如
not(contains(concat(@id, @name, text()), 'keyword'))
登录后复制
这种,因为它需要拼接字符串再进行搜索。 在某些极端情况下,如果性能真的成了问题,你可能需要考虑是否能用其他XPath函数或者在代码层面(比如Python的BeautifulSoup或lxml)进行后处理来达到同样的效果。但对于大多数Web抓取任务来说,不必过度担心
not()
登录后复制
的性能,它的简洁和表达力带来的效率提升往往远超那点微小的计算开销。

最终,掌握

not()
登录后复制
的关键在于理解它如何将“真”变为“假”,以及它在不同上下文(特别是谓词中)的作用。多写多练,自然就熟了。

以上就是XPath的not()函数怎么否定表达式?的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号