python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python XPath position()函数用法

Python XPath中position()函数的完整使用指南

作者:detayun

很多使用 lxml 做网页爬虫的开发者,都会踩中 position() 相关的经典坑,这篇文章主要为大家详细介绍了Python XPath中position()函数的语法和常见错误,希望对大家有所帮助

前言

很多使用 lxml 做网页爬虫的开发者,都会踩中 position() 相关的经典坑:

  1. 混淆 XPath 下标从 1开始,和 Python 列表下标从0开始;
  2. 不清楚 [] 作用范围,不加括号直接使用 //div[position()=2] 导致匹配失败;
  3. 分不清 position() 匹配规则:相对于当前同级节点,不是全局序号;
  4. 尝试用 position() 配合 string() 批量提取文本,出现预期外结果。

本文结合真实爬虫场景,完整讲解 position() 语法、案例、常见错误、实用业务代码。

运行环境:Python + lxml.etree

一、基础语法规则

1. position() 核心定义

position() 返回当前节点在同级兄弟节点中的位置序号,序号从 1 开始,不存在 position()=0。

语法格式:

节点选择器[position() = N]

简写等价写法(日常最常用):

节点选择器[N]

//p[2] 等价于 //p[position()=2]

2. 关键红线(90%的人踩坑)

//div[@class="box"][2]        ❌ 错误理解
(//div[@class="box"])[2]     ✅ 全局匹配集合取第二个

原理拆解

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

含义:在每一个匹配到的div内部,寻找第二个子元素,不是「全局所有匹配元素里的第二个」。

(//div[@class="box"])[2]

对应 position() 写法:

(//div[@class="box"])[position()=2]

二、分层实战案例

测试HTML样本(沿用你爬虫真实结构)

<td>
    <p>Capital Market Day 2015:
&#160;</p>
    <p>ANDRITZ GROUP, Wolfgang Leitner</p>
    <p>Schuler Group, Stefan Klebert</p>
    <p>Achievements and potentials, Martin Habert</p>
</td>

假设当前上下文节点:td_node(对应你代码里的 td_list[0])

案例1:选取第一个p标签

# 两种写法等价
p1 = td_node.xpath('./p[position()=1]')
p1 = td_node.xpath('./p[1]')

# 提取内部全部文本
title = td_node.xpath('string(./p[position()=1])').strip()

案例2:选取第2个及以后所有p标签(业务高频需求)

爬虫经常需要:标题取第一个p,数据列表取剩余全部p

./p[position() > 1]

Python完整代码:

from lxml import etree

html = """
<td>
    <p>Capital Market Day 2015:<br><br>&#160;</p>
    <p>ANDRITZ GROUP, Wolfgang Leitner</p>
    <p>Schuler Group, Stefan Klebert</p>
    <p>Achievements and potentials, Martin Habert</p>
</td>
"""
tree = etree.HTML(html)
td_node = tree.xpath('//td')[0]

# 标题:第一个p
top_title = td_node.xpath('string(./p[position()=1])').strip()

# 数据列表:position>1 所有p
p_nodes = td_node.xpath('./p[position() > 1]')
data_list = [p.xpath('string()').strip() for p in p_nodes]

print("标题:", top_title)
print("名单列表:", data_list)

输出:

标题: Capital Market Day 2015:
名单列表: ['ANDRITZ GROUP, Wolfgang Leitner', 'Schuler Group, Stefan Klebert', 'Achievements and potentials, Martin Habert']

案例3:全局匹配,取第二个class匹配元素

场景:页面存在多个 <div class="content-placement-wrapper">,取第二个

# 正确写法
(//div[@class="content-placement-wrapper"])[position()=2]

# 简写
(//div[@class="content-placement-wrapper"])[2]

接着在内部查找子元素:

(//div[@class="content-placement-wrapper"])[2]//div[@class="ci-col-1"]

案例4:position() 与 last() 搭配

last() 获取同级节点总数

选取最后一个p:

./p[position() = last()]

选取倒数第二个p:

./p[position() = last()-1]

三、高频错误汇总

错误1:使用 position()=0

./p[0]
./p[position()=0]

XPath序号从1开始,永远匹配不到任何节点,返回空列表。

很多新手把 Python 列表 [0] 的习惯直接带入 XPath,这是最常见bug。

错误2:不加括号,想用序号筛选全局结果

//div[@class="content-placement-wrapper"][2]  ❌

解读:找到每个content-placement-wrapper内部第二个子节点,不是全局第二个div。

错误3:试图使用string()批量配合position直接返回列表

# 错误写法,无法得到列表
text_list = td_node.xpath('string(./p[position()>1])')

string(节点) 只能接收单个节点,返回字符串;不能批量处理一组节点。

正确思路:先获取节点列表,再循环调用 string() 提取文本。

错误4:混淆「相对上下文position」和「全局序号」

<div class="wrap">
    <p>文本1</p>
</div>
<div class="wrap">
    <p>文本2</p>
</div>

//div/p[1]

这里的 position()=1 是每个div内部p的位置,不是页面上所有p全局排序。

四、position() 实用拓展表达式速查表

XPath表达式含义
./p[position()=1]当前节点下第一个p
./p[position()>1]当前节点下第2个及以后所有p
./p[position()<3]当前节点下第1、2个p
./p[position()=2 or position()=3]当前节点下第2、3个p
(//div[@class="box"])[3]全局所有匹配div集合中第三个
./p[position()=last()]同级最后一个p

五、爬虫通用模板(直接复制使用)

适用于你当前抓取表格 <td> 多行p标签场景

import re
from lxml import etree

def clean_text(raw_text):
    """清理不间断空格、换行、多余空白"""
    if not raw_text:
        return ""
    return re.sub(r"\s+", " ", raw_text).strip()

html = """你的网页html"""
tree = etree.HTML(html)
td_list = tree.xpath("//td")

for td in td_list:
    # 标题:第一个p
    title_raw = td.xpath('string(./p[position()=1])')
    title = clean_text(title_raw)

    # 内容列表:排除第一个p,剩下全部
    p_node_list = td.xpath('./p[position() > 1]')
    content_list = [clean_text(p.xpath('string()')) for p in p_node_list]

    print("标题:", title)
    print("内容:", content_list)

六、总结

  1. XPath position() 起始序号 1,杜绝 [0];
  2. [] 筛选作用域很关键:全局筛选集合取值必须外层包裹 ();
  3. position() 是同级兄弟节点内排序,不是页面全局排序;
  4. 批量提取文本:先选出节点列表,循环执行 string(),不要试图一条xpath直接返回字符串列表;
  5. 业务中 position() > 1、position() = last() 是表格、列表爬虫高频写法。

到此这篇关于Python XPath中position()函数的完整使用指南的文章就介绍到这了,更多相关Python XPath position()函数用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文