如何用XPath获取并分组两个空pb节点间的所有节点与文本?
这是个非常实际的需求!要实现按<pb/>节点分组抓取内容,得根据你使用的XPath版本来选方案,下面分两种常见情况说明:
XPath 2.0及以上版本(推荐)
XPath 2.0+支持循环和更灵活的节点序列处理,直接用for表达式就能轻松分组:
for $pb in //pb return $pb/following-sibling::node()[not(preceding-sibling::pb[. >> $pb])]
逻辑解释:
$pb in //pb遍历所有<pb/>节点;following-sibling::node()取当前<pb/>之后的所有兄弟节点(包括文本节点、元素节点,比*更全面);not(preceding-sibling::pb[. >> $pb])过滤掉那些在下一个<pb/>之后的节点——简单说就是只保留到下一个<pb/>出现前的内容,最后一个<pb/>则会保留到文档末尾的所有内容。
这样每个<pb/>对应的结果就是你要的分组,完美匹配示例里的Group1、Group2、Group3。
XPath 1.0版本(兼容性方案)
XPath 1.0没有原生的循环和分组功能,得结合宿主语言(比如XSLT、Python的lxml、Java的DOM等)来实现。这里用Python+lxml举个实际例子:
from lxml import etree # 加载XML文档 tree = etree.parse("your_xml_file.xml") # 获取所有<pb/>节点的列表 pb_nodes = tree.xpath("//pb") # 循环处理每个<pb/>对应的分组 for idx, current_pb in enumerate(pb_nodes): if idx < len(pb_nodes) - 1: # 不是最后一个<pb/>,取到下一个<pb/>之前的内容 group = current_pb.xpath(""" following-sibling::node()[ count(preceding-sibling::pb) = count(current()/preceding-sibling::pb) + 1 ] """) else: # 最后一个<pb/>,取到文档末尾的所有内容 group = current_pb.xpath("following-sibling::node()") # 输出分组内容(这里可以换成你自己的处理逻辑) print(f"=== Group {idx+1} ===") for node in group: print(etree.tostring(node, encoding="unicode").strip())
XPath 1.0表达式逻辑:
count(preceding-sibling::pb) = count(current()/preceding-sibling::pb) + 1 意思是:当前节点前面的<pb/>数量,正好比当前<pb/>前面的<pb/>数量多1——这就保证了节点是在当前<pb/>之后、下一个<pb/>之前的范围里。
额外注意点
- 如果
<pb/>不是同一父节点的兄弟节点(比如嵌套在不同元素里),把following-sibling::node()换成following::node(),同时调整过滤条件避免跨层级的<pb/>干扰; - 如果不需要注释、处理指令这类节点,可以在表达式里加
not(self::comment()) and not(self::processing-instruction())来过滤; - 文本节点会被完整保留,包括空白字符,要是需要清理可以在宿主语言里做额外处理。
内容的提问来源于stack exchange,提问作者zypro
相关产品推荐
相关产品推荐

