0

0

XPath的except运算符如何求差集?

畫卷琴夢

畫卷琴夢

发布时间:2025-08-16 22:47:01

|

467人浏览过

|

来源于php中文网

原创

except运算符用于求两个节点集的差集,返回第一个节点集中不在第二个节点集中的节点,语法为“节点集A except 节点集B”,适用于XPath 2.0及以上版本;在XPath 1.0中可通过[not()]谓词实现类似效果,如//p[not(@id='p2')];与union(并集)和intersect(交集)共同构成XPath集合操作体系,广泛应用于网页抓取中的内容清洗与干扰元素排除。

xpath的except运算符如何求差集?

XPath中的

except
运算符,简单来说,就是用来找出两个节点集之间独有的部分,也就是我们常说的“差集”。它会返回第一个节点集中存在,但第二个节点集中不存在的所有节点。

当我第一次接触到

except
这个操作符时,脑子里浮现的其实是数据库里SQL的
except
。它们的核心理念是相通的:从一个集合里剔除另一个集合的成员。在XPath里,这尤其有用,比如你想选定页面上所有链接,但又不包括那些指向外部网站的。或者,你可能想抓取所有段落,但要排除掉那些包含特定广告类的。

它的基本语法是:

节点集A except 节点集B
。 这会返回一个全新的节点集,其中包含了所有属于
节点集A
,但却不属于
节点集B
的节点。

举个例子,假设我们有这样的HTML结构:

这是一个段落。

这是一个span。

这是另一个段落。

内部链接 外部链接

如果我们想选择所有

p
元素,但排除掉
id
p2
的那个,可以这样写:
//p except //p[@id='p2']
这个表达式会返回

这是一个段落。

再来一个更复杂的场景,比如我们想获取所有的

div
子元素,但又不想包含那些有
class="header"
div
。 假设HTML是:


  
Header 1
Content 1
Header 2
Content 2

XPath可以写成:

//div[@class='main']/* except //div[@class='header']
这里,
//div[@class='main']/*
会选出所有
main
div
下的直接子元素(包括header和普通div)。
except //div[@class='header']
则把所有
header
类的
div
从结果中剔除。最终得到的就是
Content 1
Content 2

有一点需要注意,

except
操作符是XPath 2.0及以上版本才支持的。如果你在使用XPath 1.0的环境,比如一些老旧的XML解析库,或者某些浏览器内置的XPath引擎,你可能就无法直接使用它了。这时候,我们通常需要借助其他方法,比如结合
[not()]
谓词或者在编程语言层面进行过滤。这多少有点麻烦,但也不是没有办法。

XPath 1.0环境下如何实现差集操作?

这确实是个现实问题。我之前就遇到过,在一些遗留系统里,虽然XPath 2.0已经普及很久了,但它们的底层解析器依然停留在1.0版本。这时候

except
就用不上了,你得换个思路。

最常见的替代方案是利用谓词(predicates)中的

not()
函数。它的逻辑是“选择所有满足条件A的节点,并且这些节点不满足条件B”。 语法通常是:
节点集A[not(条件B)]

举个例子,还是刚才那个需求:选择所有

p
元素,但排除掉
id
p2
的。 在XPath 1.0中,你可以这样写:
//p[not(@id='p2')]
这会非常精准地选出所有
p
元素中,那些
id
属性不等于
p2
的。结果和
//p except //p[@id='p2']
是完全一样的。

再看那个剔除

header
div
的例子: HTML:


  
Header 1
Content 1
Header 2
Content 2

XPath 1.0的写法可以是:

//div[@class='main']/*[not(@class='header')]
这个表达式会先选择
class
main
div
下的所有直接子元素,然后通过
[not(@class='header')]
过滤掉那些
class
header
的。逻辑清晰,效果一致。

这种方式虽然不如

except
直观,因为它把“排除”的逻辑融入到了过滤条件里,但它在XPath 1.0时代是标准做法,而且效率通常也不错。理解了
not()
的用法,基本就能解决大部分差集需求了。当然,如果逻辑变得非常复杂,比如要从A中排除B和C,那
not()
的嵌套或者组合可能会变得有点冗长,这时候
except
的简洁性就体现出来了。

except
union
intersect
等集合操作符的区别与联系

当我们谈论

except
的时候,很难不联想到XPath里的其他集合操作符,比如
union
(联合)和
intersect
(交集)。它们都是处理节点集的利器,但各自的侧重点和应用场景大相径庭。

Pebblely
Pebblely

AI产品图精美背景添加

下载

union
,顾名思义,就是把两个节点集的内容合并起来。它的语法是
节点集A | 节点集B
。这个操作符会返回所有在
节点集A
中或者在
节点集B
中的节点,并且会自动去重。比如说,你想选出页面上所有的
h1
h2
标题,你就可以写
//h1 | //h2
。它就像是SQL里的
union
或者数学里的并集。

intersect
(交集)则刚好相反,它会找出两个节点集共同拥有的节点。语法是
节点集A intersect 节点集B
。比如,你想找到所有同时具有
class="active"
class="selected"
div
元素,你可能会先选出所有
class="active"
div
,再选出所有
class="selected"
div
,然后用
intersect
找出它们的交集。在实际应用中,
intersect
用的频率可能不如
except
union
高,但它在需要精确匹配多个条件的场景下非常有用。

except
,我们已经详细讨论过了,它求的是差集,即从第一个集合中移除第二个集合共有的部分。

这三个操作符,

union
intersect
except
,共同构成了XPath 2.0强大的集合运算能力。它们让我们可以像操作数学集合一样来处理XML/HTML文档中的节点,极大地提高了XPath表达式的表达力和灵活性。我个人觉得,理解并熟练运用这些集合操作符,是掌握高级XPath技巧的关键一步。它们让原本需要多步筛选或者复杂逻辑才能实现的需求,变得一行代码就能搞定,效率提升是显而易见的。有时候,我甚至会把它们想象成数据处理管道中的不同阀门,各自完成特定的过滤或合并任务。

except
操作符在实际网页抓取中的应用案例

理论知识学得再好,最终还是要落到实际应用上。

except
操作符在网页抓取(Web Scraping)领域简直是神器般的存在,它能帮我们高效地剔除那些不想要的、干扰数据。

一个非常典型的场景是内容清洗。 想象一下,你正在抓取一个新闻网站的文章内容。通常,文章主体会被放在一个特定的

div
或者
article
标签里。但在这个主体内容里,可能混杂着各种广告、推荐阅读、版权声明或者社交分享按钮,这些都不是你真正关心的文章文本。

假设文章内容在

里,而其中有一些广告块是
,或者图片说明是
。 如果你直接抓取
//div[@id='article-body']//text()
,你可能会把广告文案和图片说明也抓进来。 这时候,
except
就能派上用场了:
//div[@id='article-body']//*[not(self::script or self::style)] except //div[@class='ad-block'] except //figcaption
这个表达式的思路是:

  1. 先选出
    article-body
    下所有非脚本非样式的元素(
    //*[not(self::script or self::style)]
    ,这是为了避免抓取JS代码或CSS样式)。
  2. 从中剔除所有
    ad-block
    类的
    div
  3. 再剔除所有
    figcaption
    元素。 这样,剩下的就是相对纯净的文章内容了。当然,你可能还需要进一步处理文本,比如去除多余的空格和换行符。

另一个例子是导航菜单的排除。 你可能想抓取页面上所有的链接,但是导航菜单里的链接往往是重复的或者功能性的,你只想要正文或者侧边栏里的链接。 假设导航菜单在

标签里:
//a except //nav//a
这会选中页面上所有
标签,然后排除掉所有在
标签内部的
标签。是不是很简洁?

有时候,网站会有一些通用的模板元素,比如页脚(footer)或者侧边栏(sidebar),它们包含一些你不想重复抓取的信息。如果这些元素有明确的标识(ID或Class),你就可以用

except
把它们从你的目标节点集中剔除。 例如,抓取所有
div
但排除页脚和侧边栏:
//div except //footer//div except //aside//div

这些应用场景都体现了

except
的强大之处:它提供了一种声明式的方式来定义“不想要什么”,而不是“只想要什么”,这在面对复杂或不规则的HTML结构时,往往能带来意想不到的便利和效率。它让我能更专注于核心数据的提取,而不是纠结于如何绕过那些干扰元素。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

683

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

323

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

348

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1096

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

358

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

697

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

577

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

418

2024.04.29

AO3中文版入口地址大全
AO3中文版入口地址大全

本专题整合了AO3中文版入口地址大全,阅读专题下面的的文章了解更多详细内容。

1

2026.01.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 0.8万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3万人学习

CSS教程
CSS教程

共754课时 | 21.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

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