通过xpath实现if...else...逻辑

最近学习xpath(1.0)相关知识,在做数据抓取匹配的时候遇到一个问题,几经折腾解决了问题,记录下来分享给大家。

问题: 这是待匹配的数据,要抓取的数据在span标签class= ellipsis vip里面,【第一百一十二章 文章标题,该章节已被锁定】这两个文章标题,但是我们可以看到两个span标签里面的内容不一致,其中第二个span里面还有个span。

xml 复制代码
<dl class="Volume">
   <dt>
      <a class="folding" href="javascript:">- 收起</a>
      <span class="tit">正文</span><span class="info">已更新1111章</span>
   </dt>
   <dd>
         <a target="_blank"
            href="/chapter/1111/aaaa.html">
               <span class="ellipsis vip">
                     第一百一十二章 文章标题
               </span>
         </a>
         <a target="_blank"
            href="/chapter/2222/bbbb.html">
               <span class="ellipsis vip">
                     <span class="red">【锁】</span>
                     该章节已被锁定
               </span>
         </a>
   </dd>
</dl>

这就很麻烦了,我们写两个xpath去匹配这两种结构的数据

css 复制代码
//dl[@class="Volume"]//dd/a/span/text() //这个匹配第一个文章标题
//dl[@class="Volume"]//dd/a/span/span/following-sibling::text() //这个匹配第二个文章标题

而且代码还要加判断,选择哪种xpath匹配哪种数据,变得很繁琐。所以直接查找xpath语法是否有判断的逻辑,等于是说在xpath匹配的时候,就把判断加上了,直接写一个就行了。那究竟有没有呢?

解决问题: 通过谷歌搜索,在stackoverflow发现了这个回答: stackoverflow.com/questions/1... 里面的解决方法提出了一个这样的xpath表达式

bash 复制代码
concat(substring($stringA, 1 div $cond), substring($stringB, 1 div not($cond)))

这里有几个知识点要串联一下:

  • div 这是xpath的除法符号,1 div 1 ,就是1除以1的意思
  • not() 这个可以理解为取反,not(1)的结果就是0
  • substring 用来截取字符串,参数为substring(string,number,length),string用于指定要截取的字符串;number用于指定开始位置;length用于指定截取字符串的长度。如果缺少length参数将从开始位置number一直到截取字符串的长度。这个很重要,这个知识点要注意,比如substring("12345",1),取到的值就是12345,从第一位开始取值,substring("12345",2),取到的值就是2345,从第二位开始取值。但是如果number是个无穷大的值呢,我们的字符串都是有长度的,不可能无穷大,所以这时候就没有值可以返回了,这个是上面那个表达式实现的重点。
  • concat 用于串连多个字符串

好了,知识点讲完,我们回头来分析这个表达式,stringA,stringB我们可以理解为我们要返回的字符串,cond可以理解为我们的条件,如果cond为1,那前面的1 div 1就是1除以1等于1,后面的1 div not($cond)就变成1 div not(1),1 div 0就是1除以0,结果是个无穷大的值,这时候表达式就变成这样

bash 复制代码
concat(substring($stringA, 1), substring($stringB, 无穷大))
//substring($stringA, 1) 从第一位开始取值,等于直接返回了$stringA
//substring($stringB, 无穷大) 这个值就等于取值不了了,所以就是空,就变成了下面的表达式
concat($stringA) 
//其实就等于直接返回了$stringA这个字符串

那我们就尝试修改,看看是否能修改成上面的表达式,直接上成果

less 复制代码
//这里我把路径改短了,后面有完整代码
concat(substring(./span/span/following-sibling::text(), 1 div boolean(./span/span/following-sibling::text())),substring(./span/text(), 1 div not(./span/span/following-sibling::text())))

这个修改后整个表达式非常长,先解释逻辑,这里我们加上了个boolean,用来判断进行第一次匹配时是否满足./span/span/following-sibling::text(),也就是要匹配【该章节已被锁定】这个结果的,但是第一次其实是不满足的,所以第一个boolean(./span/span/following-sibling::text())就是返回0,第一个substring返回了空,第二个substring返回了匹配到的字符串。

接下来第二次匹配,./span/span/following-sibling::text()满足匹配,boolean(./span/span/following-sibling::text())返回1,第一个substring返回字符串。第二个substring的not(./span/span/following-sibling::text())取反返回了0,所以第二个substring没有返回字符串。

完整代码:

ini 复制代码
with open("test.html", encoding="utf-8") as f:
    data = f.read()

selector = etree.HTML(data)
lists = selector.xpath('//dl[@class="Volume"]//dd/a')

for bookList in lists:
    name = bookList.xpath('concat(substring(./span/span/following-sibling::text(), 1 div boolean(./span/span/following-sibling::text())),substring(./span/text(), 1 div not(./span/span/following-sibling::text())))').strip()
    print(name)
相关推荐
猫头虎4 小时前
PyCharm 2025.3 最新变化:值得更新吗?
ide·爬虫·python·pycharm·beautifulsoup·ai编程·pip
有味道的男人5 小时前
1688数据采集:官方API与网页爬虫实战指南
java·服务器·爬虫
有味道的男人5 小时前
Python 爬虫框架设计:类封装与工程化实践
开发语言·爬虫·python
老王Bingo15 小时前
Qwen Code + Chrome DevTools MCP,让爬虫、数据采集、自动化测试效率提升 100 倍
前端·爬虫·chrome devtools
道法自然|~18 小时前
【PHP】简单的脚本/扫描器拦截与重要文件保护
开发语言·爬虫·php
小尘要自信20 小时前
Bright Data AI Scraper Studio:企业级AI爬虫解决方案,让数据采集更智能
人工智能·爬虫·通过ai自动化爬虫·prompt生产爬虫·云端爬虫平台
失败又激情的man1 天前
爬虫逆向之观安(观镜WEB应用安全防护系统)
爬虫
小心我捶你啊1 天前
正向代理与反向代理两者的核心区别
网络·爬虫·网络协议
冰敷逆向2 天前
苏宁滑块VMP深入剖析(一):解混淆篇
javascript·爬虫·安全·web
APIshop2 天前
Java爬虫1688详情api接口实战解析
java·开发语言·爬虫