技术问询:使用XPath获取页面两集合交集及多组标签间文本
我来帮你解决这两个XPath问题,都是网页爬取里很常见的场景:
1. 用XPath获取两个集合的所有交集
这个需求的实现要看你用的是XPath 2.0及以上版本,还是只能用XPath 1.0(很多老工具或库还在依赖1.0):
- XPath 2.0+ 写法(推荐,更简洁):
直接用intersect运算符,它会返回两个节点集合的交集。比如你要找同时属于//div[@class='group1']和//div[@class='group2']的节点,写法是:
//div[@class='group1'] intersect //div[@class='group2']
- XPath 1.0 写法(兼容旧环境):
1.0没有intersect,可以用谓语筛选。如果两个集合的节点有共同属性,直接用多条件判断:
//div[@class='group1' and @class='group2']
如果是更通用的交集(比如两个集合没有共同属性,只是节点重叠),可以用集合计数的技巧:
//div[@class='group1'][count(. | //div[@class='group2']) = count(//div[@class='group2'])]
原理是:如果当前节点属于第二个集合,那么两个集合的并集大小等于第二个集合的大小。
2. 获取多组标签对之间的文本
你的现有代码是针对单组a[@name="dst100030"]和a[@name="dst100031"]之间的pre文本,要批量处理多组的话,分两种情况:
情况1:使用XPath 2.0+(支持循环和变量)
如果你的爬虫工具支持XPath 2.0(比如Saxon、lxml的某些配置),可以用for循环遍历所有起始的a标签,自动匹配对应的结束a标签,一次性获取所有组的文本:
假设页面里的a标签是按起始-结束-起始-结束的顺序排列的(比如dst100030→dst100031→dst100032→dst100033...),写法可以是:
for $start_a in //a[@name][position() mod 2 = 1] return //pre[ preceding-sibling::a[@name = $start_a/@name] and following-sibling::a[@name = $start_a/following-sibling::a[1]/@name] ]//text()
或者更简洁的写法(如果每组的pre正好在起始a和下一个a之间):
for $a in //a[@name] return if ($a/following-sibling::a) then $a/following-sibling::pre[1]//text() else ()
这个写法会遍历每个a标签,取它后面第一个pre的文本(如果后面还有下一个a的话),正好对应每组标签对之间的内容。
情况2:只能用XPath 1.0(无循环支持)
XPath 1.0不支持变量和循环,这种情况下确实需要先提取所有a标签的@name值列表,然后逐个替换到你的原有XPath中,循环处理每组。比如先获取所有name值:
//a/@name
得到列表后,遍历每一对连续的name(比如第1和第2个,第3和第4个...),替换成你原来的XPath模板:
//pre[preceding-sibling::a[@name="替换为起始name"] and following-sibling::a[@name="替换为结束name"]]//text()
内容的提问来源于stack exchange,提问作者JBJ
相关产品推荐
相关产品推荐

