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

XPath中的
except
运算符,简单来说,就是用来找出两个节点集之间独有的部分,也就是我们常说的“差集”。它会返回第一个节点集中存在,但第二个节点集中不存在的所有节点。
当我第一次接触到
except
这个操作符时,脑子里浮现的其实是数据库里SQL的
except
。它们的核心理念是相通的:从一个集合里剔除另一个集合的成员。在XPath里,这尤其有用,比如你想选定页面上所有链接,但又不包括那些指向外部网站的。或者,你可能想抓取所有段落,但要排除掉那些包含特定广告类的。
它的基本语法是:
节点集A except 节点集B
。这会返回一个全新的节点集,其中包含了所有属于
节点集A
,但却不属于
节点集B
的节点。
举个例子,假设我们有这样的HTML结构:
如果我们想选择所有
p
元素,但排除掉
id
为
p2
的那个,可以这样写:
//p except //p[@id='p2']
这个表达式会返回
这是一个段落。
。
再来一个更复杂的场景,比如我们想获取所有的
div
子元素,但又不想包含那些有
class="header"
的
div
。假设HTML是:
Header 1Content 1Header 2Content 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 1Content 1Header 2Content 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
except
与
union
、
intersect
等集合操作符的区别与联系
当我们谈论
except
的时候,很难不联想到XPath里的其他集合操作符,比如
union
(联合)和
intersect
(交集)。它们都是处理节点集的利器,但各自的侧重点和应用场景大相径庭。
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
操作符在实际网页抓取中的应用案例
理论知识学得再好,最终还是要落到实际应用上。
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这个表达式的思路是:
先选出
article-body下所有非脚本非样式的元素(
//*[not(self::script or self::style)],这是为了避免抓取JS代码或CSS样式)。从中剔除所有
ad-block类的
div。再剔除所有
figcaption元素。这样,剩下的就是相对纯净的文章内容了。当然,你可能还需要进一步处理文本,比如去除多余的空格和换行符。
另一个例子是导航菜单的排除。你可能想抓取页面上所有的链接,但是导航菜单里的链接往往是重复的或者功能性的,你只想要正文或者侧边栏里的链接。假设导航菜单在
标签里:
//a except //nav//a这会选中页面上所有
标签,然后排除掉所有在
标签内部的
标签。是不是很简洁?
有时候,网站会有一些通用的模板元素,比如页脚(footer)或者侧边栏(sidebar),它们包含一些你不想重复抓取的信息。如果这些元素有明确的标识(ID或Class),你就可以用
except把它们从你的目标节点集中剔除。例如,抓取所有
div但排除页脚和侧边栏:
//div except //footer//div except //aside//div这些应用场景都体现了
except的强大之处:它提供了一种声明式的方式来定义“不想要什么”,而不是“只想要什么”,这在面对复杂或不规则的HTML结构时,往往能带来意想不到的便利和效率。它让我能更专注于核心数据的提取,而不是纠结于如何绕过那些干扰元素。
以上就是XPath的except运算符如何求差集?的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1430299.html赞 (0)打赏微信扫一扫
支付宝扫一扫
微信扫一扫
支付宝扫一扫