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:: 是日常爬虫解决无特征元素最核心的两个语法。

掌握这套思路,绝大多数缺乏标记的网页节点都可以稳定定位,大幅提升爬虫鲁棒性,减少页面改版带来的维护成本。

相关推荐
艾莉丝努力练剑3 小时前
【QT:解决问题】Qt5Core.dll:无法定位程序输入点
java·开发语言·qt·学习·面试
流浪0013 小时前
Python 基础语法(一):常量、变量、输入输出与运算符
开发语言·python
光影少年3 小时前
react离线缓存、图片缓存方案
开发语言·前端·javascript·react native·react.js·缓存·前端框架
鸿芯微控科技3 小时前
MFC关断后还有流量怎么办?零流量、阀门泄漏、压差与Python分析
c++·python·mfc·质量流量控制器·关断泄漏·零流量测试
SomeB1oody3 小时前
【RustyML入门】3.7. 循环层
开发语言·后端·机器学习·rust·教程
点云-激光雷达-Slam-三维牙齿6 小时前
速度起飞 笔记本电脑6G显卡llama运行Qwen3.6 35BA3B MTP 大模型
人工智能·python·电脑·llama
言乐66 小时前
Python游戏水平测试辅助系统
开发语言·python·游戏·django·pygame
从零开始学习人工智能13 小时前
【踩坑实录】WSL2 解决 onnxruntime\-gpu ImportError: libcudart\.so\.13 无 CUDA13 运行库问题
python
WWJA王文举13 小时前
I²C通信完整流程详解:START、地址、ACK、数据、Repeated START和STOP一次讲透
c语言·开发语言