如何在XPath查询中排除元素?以BBC美食网站食材列表为例
解决BBC Good Food食材列表提取时排除Tooltip文本的问题
嘿,我来帮你搞定这个提取食材文本的麻烦事儿!你的核心问题是要从带tooltip的食材列表项里提取干净的食材内容,而之前的XPath没过滤掉tooltip里的文本,咱们一步步来修正:
为什么你的原XPath不生效?
你写的//li[@class='ingredients-list__item'][not(div[@class='gf-tooltip'])]//text()有两个关键问题:
- 筛选条件
[not(div[@class='gf-tooltip'])]是在找不包含tooltip的li,但你要处理的目标li恰恰是包含这个tooltip的,所以这个条件直接把你需要的元素排除了; - 就算选对了li,
//text()会递归取出所有子节点的文本,包括tooltip里的内容,自然会拿到不需要的tooltip文本。
正确的XPath写法
方法1:过滤掉tooltip祖先的文本节点
这个XPath会选中li下所有文本节点,但自动排除那些属于tooltip的文本:
//li[@class='ingredients-list__item']//text()[not(ancestor::div[@class='gf-tooltip'])]
用Scrapy提取后,只需要把文本片段拼接并清理一下:
# 提取文本片段 text_fragments = s.xpath("//li[@class='ingredients-list__item']//text()[not(ancestor::div[@class='gf-tooltip'])]").extract() # 拼接并去除多余空格 clean_ingredient = ''.join(text_fragments).strip() # 结果就是你要的:'400g new potato, halved if large'
方法2:精准选取目标文本节点
如果你想更精准地选取li的直接文本(比如重量、备注)和a标签里的食材名称(排除tooltip),可以用这个组合XPath:
//li[@class='ingredients-list__item']/text() | //li[@class='ingredients-list__item']/a//text()[not(ancestor::div[@class='gf-tooltip'])]
这个写法是把li的直接文本和a标签里非tooltip的文本组合起来,最终结果和方法1完全一致。
测试小技巧
你可以在浏览器开发者工具的Console里用$x("你的XPath表达式")来快速验证结果,比如输入上面的XPath,就能看到返回的文本片段都是你需要的内容,没有tooltip里的文字~
内容的提问来源于stack exchange,提问作者Jamie Bull
相关产品推荐
相关产品推荐

