lxml xpath 返回空列表的常见原因包括:命名空间未声明或未正确传入namespaces参数;误用etree.xml()解析html文档;源数据含bom或编码不匹配;xpath大小写敏感;text()与string()语义混淆;属性查询遗漏@符号;全局//搜索性能低且易出错。

lxml XPath 查询返回空列表,常见原因有哪些
查不到节点不是 XPath 写错了就完事,lxml 对文档结构、命名空间、编码和解析模式非常敏感。
- XML 带命名空间但没声明或没用
namespaces参数——tree.xpath('//item')在带xmlns="http://example.com"的文档里必然为空 - HTML 文档用
etree.XML()解析(应改用etree.HTML())——XML 解析器遇到<br>或自闭合标签直接报错或跳过整段 - 源数据含 BOM 或编码不匹配(如 UTF-8 with BOM 被当 ASCII 读),导致根节点解析失败,后续所有
xpath()都返回空 - XPath 表达式本身对大小写敏感:
//DIV不会匹配<div>,HTML 场景建议统一用小写 <h3>namespaces 参数怎么传才真正生效</h3> <p>命名空间不是加个字典就行,必须和 XPath 字符串里的前缀严格对应,且前缀名可自定义,但 URI 必须一字不差。</p> <ul> <li>先从文档中提取命名空间:用 <code>tree.nsmap查看实际注册的映射,注意None键代表默认命名空间 - 给默认命名空间起别名(比如
'd'),然后在 XPath 中显式使用:tree.xpath('//d:book', namespaces={'d': 'http://purl.org/dc/elements/1.1/'}) - 如果不想改 XPath,也可用
local-name()绕过前缀:tree.xpath("//*[local-name()='book']"),但性能略低,且无法区分同名不同 ns 的元素
text() 和 string() 在 lxml 中行为差异明显
text() 是 XPath 轴,只取直接子文本节点;string() 是函数,返回节点及其后代所有文本拼接结果——二者不能混用,且返回类型不同。
-
tree.xpath('//p/text()')返回['Hello', 'World'](列表,每个是字符串) -
tree.xpath('string(//p)')返回'HelloWorld'(单个字符串,无换行/空格保留) - 想取带格式的全部文本(含子标签间的空白)?用
.xpath('normalize-space(//p)')更稳妥 - 若目标是获取某个属性值,别漏掉
@://img/@src,写成//img/src会返回空
大规模 XML 解析时,XPath 性能卡在哪
瓶颈往往不在 XPath 引擎本身,而在树构建和重复查询方式上。
立即学习“Python免费学习笔记(深入)”;
- 避免对同一节点反复调用
xpath():先用tree.xpath('//section')获取节点列表,再对每个section调用.xpath('.//title')(注意开头的.) - 不用
//开头的全局搜索查深层固定路径:比如已知结构是/root/data/item/title,就写/root/data/item/title,比//title快一个数量级 - 大文件慎用
etree.parse()全加载;改用etree.iterparse()边解析边处理,配合clear()释放内存 - 纯文本提取场景,有时正则比 XPath 更快——但前提是结构稳定、不嵌套、无 CDATA 干扰
命名空间处理和 text() / string() 的语义差异,是线上排查最耗时间的两个点,尤其当别人提供的 XML 格式不规范时。










