三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Python如何使用XPath定位没有特征的元素?无id、无class通用定位技巧

Python如何使用XPath定位没有特征的元素?无id、无class通用定位技巧

前言

爬虫开发中经常遇到一类棘手场景:目标元素没有id、没有class、没有独特属性,无法直接通过属性筛选。

<div> <div>标题</div> <div>需要抓取的内容</div> <div>备注信息</div> </div>

如上代码,所有标签完全一致,没有任何特征属性。很多人第一反应只能靠页面顺序硬写路径,页面微调一行代码爬虫直接失效。

本文基于lxml(XPath1.0)整理一套无特征元素定位方案,包含层级索引、轴定位、相邻节点匹配、文本锚点等实战技巧,适用于静态网页解析,同时兼容Selenium、DrissionPage。

前置说明:lxml 仅支持 XPath 1.0,下文所有语法全部兼容 lxml。

一、环境基础模板

from lxml import etree html = """ <div class="container"> <div>文章名称</div> <div>Python XPath实战</div> <div>发布时间</div> <div>2026-08-14</div> <div>内容摘要</div> <div>无特征元素定位教程</div> </div> """ tree = etree.HTML(html)

二、方案1:位置索引定位(最简单,慎用)

基础语法

[n]代表同级第n个元素,XPath下标从1开始,不是0

//div[@class="container"]/div[2]

Python代码:

# 获取容器下第二个div target = tree.xpath('//div[@class="container"]/div[2]/text()') print(target)

⚠️ 缺点:
页面新增、删除任意同级标签,顺序发生变化,定位直接失效。
适合静态页面、结构永远不会变动的场景,不推荐作为首选方案

拓展用法:

  • [last()]匹配同级最后一个元素
//div[@class="container"]/div[last()]
  • position()>3筛选位置大于3的元素
//div[@class="container"]/div[position()>3]

三、方案2:锚点文本定位(最推荐!业务首选)

核心思路:找到旁边有固定文本的元素作为锚点,再通过轴找到目标元素
示例需求:已知文本文章名称,抓取紧跟其后的Python XPath实战

1. following-sibling:: 后续同级元素

匹配当前节点后面同级兄弟标签

//div[text()="文章名称"]/following-sibling::div[1]

Python示例:

res = tree.xpath('//div[text()="文章名称"]/following-sibling::div[1]/text()') print(res) # ['Python XPath实战']

2. preceding-sibling:: 前面同级元素

匹配当前节点前面同级兄弟标签

//div[text()="2026-08-14"]/preceding-sibling::div[1]

3. 兼容文本空格、换行问题

网页源码经常存在换行、空格,text()精确匹配容易失败,改用contains()

//div[contains(text(),"文章名称")]/following-sibling::div[1]

四、方案3:父子、祖先层级定位

1. parent:: 直接获取父节点

//div[contains(text(),"发布时间")]/parent::div

2. ancestor:: 向上查找任意祖先节点

适合多层嵌套,向上寻找指定父容器

//div[contains(text(),"2026-08-14")]/ancestor::div[@class="container"]

3. child:: 子节点(等价直接使用 /标签)

//div[@class="container"]/child::div[1]

五、方案4:组合多邻居条件,增强稳定性

页面结构复杂时,单一锚点容易匹配到多条结果,可以叠加条件过滤。
示例:找到「内容摘要」后面第一个div,并且父容器是container

//div[@class="container"]//div[contains(text(),"内容摘要")]/following-sibling::div[1]

六、方案5:多标签混合、嵌套场景实战

测试HTML结构(多层嵌套,无特征)

<div class="box"> <p>标题</p> <div> <span>无用信息</span> <span>目标内容</span> </div> </div>

需求:根据<p>标题</p>定位,找到后面div内部第二个span

//p[contains(text(),"标题")]/following-sibling::div//span[2]

七、常用XPath轴完整速查表

语法作用
following-sibling::tag当前节点之后的同级兄弟
preceding-sibling::tag当前节点之前的同级兄弟
parent::tag直接父节点
ancestor::tag所有上层祖先节点
child::tag直接子节点
following::tag文档中出现在后面所有节点(不限层级)
preceding::tag文档中出现在前面所有节点(不限层级)

区分重点:
following-sibling只找同级following查找所有后代以外后续全部节点,范围更大,尽量优先使用 sibling,减少误匹配。

八、高频踩坑汇总

坑1:XPath下标从1开始,很多人习惯写[0]

# 错误,[0]匹配不到任何元素 tree.xpath('//div[0]')

坑2:文本存在换行、空格,直接text()完全匹配失败

❌ 错误写法

//div[text()="文章名称"]

✅ 推荐写法

//div[contains(text(),"文章名称")]

坑3:混淆following-siblingfollowing

sibling 仅限同级,范围更小,不容易匹配到页面其他位置元素,稳定性更高。

坑4:页面动态渲染(JS生成内容)

lxml只能解析静态HTML。如果元素由JS渲染,lxml无法获取节点,需要使用DrissionPage、Selenium加载页面后再执行XPath。

坑5:匹配结果为空列表直接下标取值

# 危险,找不到元素会直接报错 data = tree.xpath('xxx')[0] # 规范写法 result = tree.xpath('xxx') if result: data = result[0]

九、实战完整示例(可直接复制运行)

from lxml import etree html = """ <div class="info"> <div>用户名</div> <div>张三</div> <div>手机号</div> <div>13800138000</div> </div> """ tree = etree.HTML(html) # 通过锚点文本,抓取后面无特征div username = tree.xpath('//div[contains(text(),"用户名")]/following-sibling::div[1]/text()') phone = tree.xpath('//div[contains(text(),"手机号")]/following-sibling::div[1]/text()') print("用户名:", username[0] if username else "") print("手机号:", phone[0] if phone else "")

十、方案选型建议

  1. 优先方案:锚点文本 + following-sibling / preceding-sibling
    页面少量增减其他无关标签,只要锚点文本不变,定位依然有效,稳定性最强。
  2. 备选方案:位置索引[n]
    仅用于页面结构永久固定、不会改版的场景。
  3. 兜底方案:多层祖先路径组合
    利用外层父容器特征,缩小查找范围,降低误匹配概率。

总结

面对没有id、class、独有属性的元素,不要直接硬编码顺序路径。
核心思想:利用页面上有稳定特征的周边元素作为锚点,借助XPath轴语法实现跨节点定位
following-sibling::preceding-sibling::是日常爬虫解决无特征元素最核心的两个语法。
掌握这套思路,绝大多数缺乏标记的网页节点都可以稳定定位,大幅提升爬虫鲁棒性,减少页面改版带来的维护成本。

← 返回列表