您现在的位置是:网站首页 > 博客日记 >

xpath学习笔记

作者:YXN-python 阅读量:189 发布日期:2024-05-15

xpath语法

Xpath的使用

pip install lxml

关键点总结

  • 路径符号:/ 表示直接子节点,// 表示任意层级。
  • 索引从1开始:如 [1] 表示第一个节点。
  • 轴语法:通过 ancestor、following 等轴精准定位节点关系。
  • 灵活筛选:结合属性、位置、文本内容等多维度条件过滤节点。

节点选择

##### 1、全节点选择
# //* 匹配文档所有节点
//*

##### 2、指定标签节点
# //tag 选择指定标签的所有节点
//head  # 所有<head>节点

层级关系

  • 直接子节点://div/a 匹配<div>下的直接子节点<a>
  • 子孙节点://body//a 匹配<body>下任意层级的<a>

属性选择

##### 1、属性筛选
# [@attr="value"] 根据属性值筛选节点
//a[@href="image1.html"]  # href为指定值的<a>

##### 2、属性值提取
# @attr 获取节点属性值
//a/@href  # 提取所有<a>的href属性

##### 3、多值属性匹配
# contains(@attr, "value") 匹配属性包含某值的节点(如多class)
//a[contains(@class, "li")]  # class包含"li"的<a>

父节点选择

##### 简写
# /.. 选择父节点
a = html.xpath('//a/..')  # <a>的直接父节点

##### 轴语法
# parent::tag 指定父节点类型
a = html.xpath('//a/parent::div')  # <a>的父级<div>

按位置筛选

######### 1、按位置筛选(索引从1开始)
# 选择第 n 个
//tr[n]

# 最后一个
//tr[last()]  

# 选择前 n 个
//tr[position() <= n]

# 选择最后 n 个
//tr[position() >= last() - n + 1]

# 倒数第三个
//tr[last()-2]

######### 2. 选择某个区间内的
# 选择从第 m 个到第 n 个
//tr[position() >= m and position() <= n]

######### 3. 选择偶数或奇数位置的
# 选择偶数位置的
//tr[position() mod 2 = 0]

# 选择奇数位置的	
//tr[position() mod 2 = 1]

######### 4. 排除某些位置的
# 排除前 n 个
//tr[position() > n]

# 排除最后 n 个
//tr[position() <= last() - n]

######### 5. 使用范围函数(XPath 2.0+)
# 如果支持 XPath 2.0 或更高版本,可以使用 subsequence 函数来选择区间:
subsequence(//li, m, n)

轴(Axes)操作

##### 1、祖先节点
# ancestor::tag 选择祖先节点
//a/ancestor::div  # <a>的所有祖先<div>

##### 2、后代节点
# descendant::tag 选择后代节点
//a[6]/descendant::*  # 第6个<a>的所有子节点

##### 3、后续节点
# following::tag 选择文档顺序后续的所有节点
//a[1]/following::*  # 第一个<a>之后的所有节点

##### 4、同级兄弟节点
# following-sibling::tag 选择后续同级节点
//a[1]/following-sibling::a  # 第一个<a>之后的所有同级<a>

##### 5、属性轴
# attribute::* 提取所有属性
//a[1]/attribute::*  # 第一个<a>的所有属性

通配符与联合选择

##### 通配符

# *: 匹配任意元素节点
//div/*  # <div>下的所有直接子元素

# @*: 匹配任意属性
//a[@*] # 带有任意属性的<a>

# node(): 匹配任意类型节点(元素、文本、属性等)
//div/node()  # <div>下的所有子节点(含文本)

##### 联合选择器
# | 合并多个路径的结果
a = html.xpath('//div/a | //span/a')  # 同时选择<div>和<span>下的<a>

多条件组合

##### 1、逻辑与/或 and、or 组合多个条件
//a[contains(@class, "li") or @name="items"]
//a[contains(@class, "li") and @name="items"]

复杂条件与嵌套

##### 多层级条件筛选
a = html.xpath('//div[contains(@class, "container")]//a[text()="详情"]')  
# 在class含"container"的<div>下,找到文本为"详情"的<a>

##### 嵌套函数与轴结合
a = html.xpath('//a[contains(@href, "image")]/ancestor::div[1]')  
# 找到href含"image"的<a>的第一个祖先<div>

动态路径、模糊匹配、变量

##### 动态属性名
# 使用变量动态构建属性名(需结合编程语言特性):
attr = "href"
a = html.xpath(f'//a[@{attr}="image1.html"]')

##### 部分属性值匹配
# 匹配 `href` 包含 `product_` 的链接
a = html.xpath('//a[contains(@href, "product_")]/@href')

# 匹配 `id` 以 `user-` 开头的元素
a = html.xpath('//div[starts-with(@id, "user-")]/text()')

##### 正则表达式(需结合 lxml 扩展)
# 使用 `re:test()` 函数(需注册命名空间)
ns = {"re": "http://exslt.org/regular-expressions"}
a = html.xpath('//a[re:test(@href, "^/product/\d+$")]', namespaces=ns)

##### 参数化查询
# (需结合 XPath 2.0+ 或扩展库,部分环境支持)
# 假设支持外部变量(伪代码)
a = html.xpath('//a[@id=$id]', id="link1")

预编译 XPath

# 重复使用的路径可预编译提升效率:
find_links = etree.XPath('//a[@class="link"]')
a = find_links(html)

示例:

from lxml import etree

html = etree.HTML(doc)
a = html.xpath('//*')  # 所有节点
a = html.xpath('//head')  # 指定head节点
a = html.xpath('//head')  # 指定节点为列表

a = html.xpath('//div/a')  # 子节点里面的子孙节点   div>a
a = html.xpath('//body//a')  # 子节点里面的子孙节点   div>a
a = html.xpath('//body//a[@href="image1.html"]/..')  # 上一节点 div   a..
a = html.xpath('//body//a[1]/..')  # 第一个a标签..
a = html.xpath('//body//a[1]/parent::*')  # 也可以通过 parent * div
a = html.xpath('//body//a[1]/parent::div')

a = html.xpath('//body//a[@href="image1.html"]')  # 属性匹配 标签为 a
a = html.xpath('//body//a[@href="image1.html"]/text()')  # 内容获取 a标签内的内容['Name: My image 1 ']
a = html.xpath('//body//a/@href')  # 属性获取 获取所有标签里面的a
a = html.xpath('//body//a/@id')  # 属性获取
a = html.xpath('//body//a[1]/@id')  # 属性获取 从1开始不是0
a = html.xpath('//body//a[@class="li"]')  # 属性多值匹配 a 标签有多个class类,直接匹配就不可以了,需要用contains
a = html.xpath('//body//a[@name="items"]')  # 属性多值匹配 匹配a标签内有name = items的标签
a = html.xpath('//body//a[contains(@class,"li")]')  # 属性多值匹配 匹配a标签内有class = li的标签
a = html.xpath('//body//a[contains(@class, "li")]/text()')  # 属性多值匹配 匹配a标签内有class = li的标签的值
a = html.xpath('//body//a[contains(@class, "li") or @name="itmes"]/text()')  # 多属性匹配 匹配a标签内有class=li or name=itmes的内容
a = html.xpath('//body//a[contains(@class,"li") and @name="items"]/text()')  # 多属性匹配 匹配a标签内有class=li and name=itmes的内容
a = html.xpath('//a[2]/text()')  # 按序选择 查找第二个a标签的内容
a = html.xpath('//a[3]/@href')  # 按序选择 查找第三个a标签的@href内容
a = html.xpath('//a[last()]/@href')  # 按序选择 查找最后一个a标签的@href内容
a = html.xpath('//a[position()<3]/@href')  # 按序选择 查找标签位置小于3的位置
a = html.xpath('//a[last()-2]/@href')  # 按序选择 查找倒数第二个a标签
a = html.xpath('//a/ancestor::*')  # ancestor:祖先节点 使用了* 获取所有祖先节点
a = html.xpath('//a/ancestor::div')  # 获取祖先节点中的div

a = html.xpath('//a[1]/attribute::*')  # 获取第一个a标签的属性值
a = html.xpath('//a[1]/attribute::href')  # 获取第一个a标签的href属性值
a = html.xpath('//a[1]/child::*')  # 获取第一个a标签的的子节点
a = html.xpath('//a[6]/descendant::*')  # 获取第六个a标签的子节点

a = html.xpath('//a[1]/following::*')  # 获取第一个a标签之后的所有节点
a = html.xpath('//a[1]/following::*[1]/@href')  # 获取第1个a标签之后的所有节点里面的第一个href里面所有的节点

a = html.xpath('//a[1]/following-sibling::*')  # 获取第一个a标签之后所有同级节点
a = html.xpath('//a[1]/following-sibling::a')  # 获取第一个a标签之后同级a节点
a = html.xpath('//a[1]/following-sibling::*[2]')  # 获取第一个a标签之后所有同级节点第二个节点
a = html.xpath('//a[1]/following-sibling::*[2]/@href')  # 获取第一个a标签之后所有同级节点第二个节点里面href的属性

print(a)

1.依靠自己的属性,文本定位

//td[text()='Data Import']

//div[contains(@class,'cux-rightArrowIcon-on')]

//a[text()='马上注册']

//input[@type='radio' and @value='1']   # 多条件

//span[@name='bruce'][text()='bruce1'][1]   # 多条件

 //span[@id='bruce1' or text()='bruce2']   # 找出多个

 //span[text()='bruce1' and text()='bruce2']    #找出多个

2.依靠父节点定位

//div[@class='x-grid-col-name x-grid-cell-inner']/div

//div[@id='dynamicGridTestInstanceformclearuxformdiv']/div

//div[@id='test']/input

3.依靠子节点定位

//div[div[@id='navigation']]

//div[div[@name='listType']]

//div[p[@name='testname']]

4.混合型

//div[div[@name='listType']]//img

//td[a//font[contains(text(),'seleleium2从零开始 视屏')]]//input[@type='checkbox']   #   包含特定文本的字体元素内的复选框输入元素

5.进阶部分

 //input[@id='123']/following-sibling::input  # 找下一个兄弟节点

 //input[@id='123']/preceding-sibling::span  # 上一个兄弟节点

 //input[starts-with(@id,'123')]  # 以什么开头

 //span[not(contains(text(),'xpath'))]  # 不包含xpath字段的span

6.索引

//div/input[2]

//div[@id='position']/span[3]

//div[@id='position']/span[position()=3]

//div[@id='position']/span[position()>3]

//div[@id='position']/span[position()<3]

//div[@id='position']/span[last()]

//div[@id='position']/span[last()-1]

7.substring 截取判断

<div data-for="result" id="swfEveryCookieWrap"></div>
//*[substring(@id,4,5)='Every']/@id  # 截取该属性 定位3,取长度5的字符 

//*[substring(@id,4)='EveryCookieWrap']  # 截取该属性从定位3 到最后的字符 

//*[substring-before(@id,'C')='swfEvery']/@id  # 属性 'C'之前的字符匹配

//*[substring-after(@id,'C')='ookieWrap']/@id  # 属性'C之后的字符匹配

8.通配符*

//span[@*='bruce']

//*[@name='bruce']

9.轴

//div[span[text()='+++current node']]/parent::div  # 找父节点

//div[span[text()='+++current node']]/ancestor::div  # 找祖先节点

10.孙子节点

//div[span[text()='current note']]/descendant::div/span[text()='123']

//div[span[text()='current note']]//div/span[text()='123'] #  两个表达的意思一样

11.following pre

//span[@class="fk fk_cur"]/../following::a   # 往下的所有a

//span[@class="fk fk_cur"]/../preceding::a[1]  # 往上的所有a

xpath提取多个标签下的text

如果我有一百段这样类似的html代码,内部的标签不固定,又如何使用xpath表达式,以最快最方便的方式提取出来?

对于如下的代码:

<div id="test3">
	我左青龙,
	<span id="tiger">
		右白虎,
        <ul>上朱雀,
            <li>下玄武。</li>
        </ul>
        老牛在当中,
	</span>
	龙头在胸口。
<div>

使用xpath的string(.)

data = selector.xpath('//div[@id="test3"]')
info = data.xpath('string(.)').extract()[0]

这样,就可以把“我左青龙,右白虎,上朱雀,下玄武。老牛在当中,龙头在胸口”整个句子提取出来,赋值给info变量。

模糊查询 contains

目前许多web框架,都是动态生成界面的元素id,因此在每次操作相同界面时,ID都是变化的,这样为自动化测试造成了一定的影响。

<div class="eleWrapper" title="请输入用户名">
	<input type="text" class="textfield" name="ID9sLJQnkQyLGLhYShhlJ6gPzHLgvhpKpLzp2Tyh4hyb1b4pnvzxFR!-166749344!1357374592067" id="nt1357374592068"  />
</div>

解决方法使用xpath的匹配功能,//input[contains(@id,'nt')]

测试使用的XML

<Root>
    <Person ID="1001">
        <Name lang="zh-cn">张城斌</Name>
        <Email xmlns="www.quicklearn.cn">cbcye@live.com</Email>
        <Blog>http://cbcye.cnblogs.com</Blog>
    </Person>
    <Person ID="1002">
        <Name lang="en">Gary Zhang</Name>
        <Email xmlns="www.quicklearn.cn">GaryZhang@cbcye.com</Email>
        <Blog>http://www.quicklearn.cn</Blog>
    </Person>
</Root>

查询所有Blog节点值中带有 cn 字符串的Person节点Xpath表达式:

/Root//Person[contains(Blog,'cn')]

2.查询所有Blog节点值中带有 cn 字符串并且属性ID值中有01的Person节点

Xpath表达式:

/Root//Person[contains(Blog,'cn') and contains(@ID,'01')]

内置函数

1. 字符串处理函数

string()

将任意类型转换为字符串

string(//div[@id="title"])  // 返回该 div 的文本内容

concat()

拼接多个字符串

concat(//span[@class="first"], "-", //span[@class="last"])   // 输出类似 "John-Doe"

substring()

截取子字符串

语法: substring(字符串, 起始位置, 长度)

substring("Hello World", 7, 5)  // 返回 "World"

contains()

判断字符串是否包含子串

//div[contains(@class, "active")]  // 选择 class 包含 "active" 的 div

starts-with()

判断字符串是否以某子串开头

//a[starts-with(@href, "https://")]  // 选择 href 以 https 开头的链接

normalize-space()

去除字符串首尾空格,并将连续空格替换为单个空格

normalize-space(//p)  // 清理段落文本中的多余空格

2. 数值处理函数

sum()

计算节点集的数值总和

sum(//item/price)  // 计算所有 price 元素的和

floor(), ceiling(), round()

数值取整

round(3.6)    // 4
floor(3.6)    // 3
ceiling(3.2)  // 4

3. 布尔函数

not()

取反布尔值

//input[not(@disabled)]  // 选择未禁用的输入框

boolean()

将值转换为布尔值

boolean(//error)  // 如果存在 error 节点返回 true

4. 节点集函数

position()

返回节点在节点集中的位置(从 1 开始)

//li[position() = 1]  // 选择第一个 li 元素

last()

返回节点集的最后一个位置

//li[last()]          // 选择最后一个 li 元素
//li[position() = last() - 1]  // 选择倒数第二个 li

count()

统计节点数量

count(//div[@class='item']) //统计类名为 item 的 <div> 元素数量 

5. 高级函数( 2.0+)

注意:部分库(如 Python lxml)仅支持  1.0,以下函数可能不可用。

tokenize()

按正则表达式分割字符串

tokenize("2023-10-01", "-")  // 返回 ["2023", "10", "01"]

matches()

正则表达式匹配

//div[matches(text(), "\d+")]  // 选择包含数字的 div

upper-case(), lower-case()

转换大小写

upper-case("Hello")  // "HELLO"

6. 实用技巧

组合函数

//div[contains(normalize-space(@class), "main")]  
// 清理 class 的空格后判断是否包含 "main"

动态路径

//book[author = //author[name='J.K. Rowling']]/title  
// 嵌套选择作者为 "J.K. Rowling" 的书籍标题

注意事项

  1. 版本兼容性: XPath 1.0 和 2.0 的函数支持差异较大,需确认运行环境。
  2. 转义字符: 如 contains(text(), "Café") 需注意编码问题。
  3. 性能: 复杂函数可能影响查询效率,尽量简化表达式。

 

YXN-python

2024-05-15