XPath在lxml中取不到嵌套子节点,主要是因上下文定位错误:不加.会从文档根查找,应使用.//span;且HTML解析须用etree.HTMLParser()而非XMLParser()。

为什么 xpath 在 lxml 里取不到嵌套子节点?
常见现象是:用 //div[@class="item"] 能拿到父节点,但接着对每个父节点调用 .xpath(".//span") 却返回空列表。问题出在 XPath 的上下文定位——. 表示当前节点,但如果你忘了加这个点,写成 //span,就会从整个文档根重新查,而不是从当前节点往下找。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 所有相对路径必须以
.开头,比如.//a/@href、./h2/text() - 用
etree.Element.xpath()方法时,传入的字符串默认是相对于该元素的,不加.也行,但加了更明确、不易误读 - 如果父节点本身是通过
find()或findall()拿到的(非 XPath),再用 XPath 查子节点时,.就不能省
lxml 解析 HTML 后 xpath 匹配失败的典型原因
不是语法写错了,而是解析器没选对。HTML 常含不规范标签(如自闭合 <img alt="Python爬虫怎么用XPath_lxml库与XPath语法提取嵌套层级节点数据" > 缺少 /、错位的 ),etree.XMLParser() 会直接报错或截断;而 etree.HTMLParser() 才能容错解析。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 永远用
etree.parse(url, etree.HTMLParser())或etree.fromstring(html_bytes, etree.HTMLParser()) - 别用
etree.XML()处理网页源码,哪怕它看起来“结构完整” - 如果遇到
XMLSyntaxError,八成是解析器类型不对,不是 XPath 写得有问题
提取文本、属性、嵌套结构时的 xpath 写法差异
xpath 返回的是列表,即使只匹配一个节点。容易忽略的是:文本内容要显式用 /text(),属性要用 /@xxx,而子元素直接写路径就行。三者混用时顺序和括号容易出错。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 取链接地址:
node.xpath("./a/@href")[0] if node.xpath("./a/@href") else None - 取标题文字:
" ".join(node.xpath("./h3/text()")).strip()(注意可能有多个文本节点) - 取带子节点的完整 HTML:
etree.tostring(node.xpath("./div[@class='content']")[0], encoding="unicode", method="html") - 避免直接对空列表索引,先判空再取
[0]
嵌套层级深时性能与可维护性怎么平衡?
写一长串 ./div/div/div/ul/li/a/text() 看起来直白,但一旦页面微调就全崩;拆成多步又怕慢。实际上 lxml 的 XPath 引擎很快,真正拖慢的是反复调用 xpath() 和无谓的异常捕获。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 优先用单次 XPath 定位到底,比如
//article//header/h1/text(),比先取article再在里面查header更稳更快 - 避免在循环里重复编译 XPath 表达式,Python 的
lxml不自动缓存,可提前提前用etree.XPath()编译一次复用 - 深层嵌套常意味着结构不稳定,建议配合
contains(@class, "title")或normalize-space()提高容错性
最麻烦的不是写不对,而是页面改版后 XPath 还能跑通、但取到了错误位置的数据——这种静默错误比直接报错更难排查。










