lXml与XPath的使用
什么是XPath?
XPath,全称为XML Path language,是一种用于在XML文档中查找节点的语言,广泛应用于网页元素的定位。
在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点,在使用传统的id与class进行匹配时往往会遇到动态的元素出现匹配不到的问题,由此一般使用xpath进行节点的精确匹配。
XPath初体验
在学习xpath之前,我们先了解一下xml的结构:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>
在这个xml中,有这些节点:
bookstore (文档节点)
lang="en" (属性节点)
节点中的值我们一般称为基本值,节点中存在父子与同胞的对应关系,例如bookstore是book/title/year/author等标签的父标签,book是bookstore的子标签等等,根据层级还可以分为先辈、后代等关系,但在匹配中一般只关心父子关系。
Xpath基本语法
在上文我们已经了解了xml标签以及它的基本对应关系,依照对应关系我们来学习xpath的基本语法
XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的,下面是它的基本用法:
| 表达式 | 描述 |
|---|---|
| 节点名 | 选取此节点的所有子节点。 |
| / | 从根节点选取(取子节点)。 |
| . | 选取当前节点 |
| .. | 选取当前节点的父节点 |
| // | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置(取子孙节点)。 |
| @ | 选择对应属性的节点 |
| 谓语:类似于编程语言中的数组下标,用于选择返回匹配集合中的第n个元素节点,不同的是它可以执行一些运算,下面是一些简单的表达式: |
| 表达式 | 描述 |
|---|---|
| /父节点/子节点集合[1] | 选取子节点集合中的第一个元素 |
| /父节点/子节点集合[last()] | 选取子节点集合的最后一个元素 |
| /父节点/子节点集合[last()-1] | 选取子节点集合的倒数第二个元素 |
| /父节点/子节点[position()❤️] | 选取子节点集合的前两个元素 |
| //选取的标签名[@元素名] | 选取所有带有元素名的对应标签元素 |
| //选取的标签名[@属性名='属性值'] | 选取所有属性名为对应属性值的对应标签元素 |
| /父节点/子节点集合[子节点的子节点值>35.00] | 选取所有子节点的子节点值大于35的元素 |
| /父节点/子节点集合[指定子节点的子节点值>35.00]//指定元素 | 选取父节点元素中的子节点元素的所有指定元素,且其中的指定子节点元素的值须大于 35.00 |
| 通配符: |
| 通配符 | 描述 |
|---|---|
| * | 匹配任何节点 |
| @* | 匹配任何属性节点 |
| node() | 匹配任何类型节点 |
| 除了这些,xpath还可以进行与计算,同时选取多种条件的节点如选取所有p与div标签:**//p | //div** |
快捷选取xpath
实际开发中,在网页代码过多在摸不清文档的节点时,我们还可以活用浏览器的开发者工具,通过定位元素功能选取元素代码,并使用右键复制XPath来获取对应元素的XPath
lxml:XPath活学活用
在初步了解了xpath的语法之后,我们开始结合python来使用它
先决准备
首先安装lxml,lxml是一个匹配库,用于对xml文档内容应用xpath语法
pip install lxml
然后,我们需要引入它的etree模块
#python lxml示例
from lxml import etree
etree是lxml专门用于解析xml与html的模块,带有常见的修正格式与应用xpath等功能。
为了使用xpath,我们还需要准备一个Html字符串用于匹配清洗:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XPath 测试文档</title>
</head>
<body>
<div id="app">
<!-- 模块 1 -->
<div id="module1" class="module" data-info="info1">
<p class="title">我是模块 1</p>
<ul>
<li>列表项 1</li>
<li>列表项 2</li>
<li>列表项 3</li>
</ul>
</div>
<!-- 模块 2 -->
<div id="module2" class="module" data-info="info2">
<p class="title">我是模块 2</p>
<ul>
<li>列表项 A</li>
<li>列表项 B</li>
<li>列表项 C</li>
</ul>
</div>
<!-- 模块 3 (含条件判断) -->
<div id="module3" class="module" data-info="info3">
<p class="title">我是模块 3</p>
<p class="description">描述文本</p>
<a href="https://example.com" id="link">点击我</a>
</div>
<!-- 数据展示区域 -->
<div id="datav">
<div id="data">
我是一段数据
<span class="highlight">高亮内容</span>
</div>
<div id="additionalData" class="additional">
额外的数据
</div>
</div>
</div>
<!-- 页脚区域 -->
<footer>
<p>© 2025 测试公司</p>
<p id="footer-note">这是页脚备注</p>
</footer>
</body>
</html>
接着我们复制这一段测试文档,加上三引号转义为字符串并转义为xpath可处理的elment对象:
from lxml import etree
html_test="""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>XPath 测试文档</title>
</head>
<body>
<div id="app"> <!-- 模块 1 --> <div id="module1" class="module" data-info="info1"> <p class="title">我是模块 1</p> <ul> <li>列表项 1</li> <li>列表项 2</li> <li>列表项 3</li> </ul> </div>
<!-- 模块 2 --> <div id="module2" class="module" data-info="info2"> <p class="title">我是模块 2</p> <ul> <li>列表项 A</li> <li>列表项 B</li> <li>列表项 C</li> </ul> </div>
<!-- 模块 3 (含条件判断) -->
<div id="module3" class="module" data-info="info3"> <p class="title">我是模块 3</p> <p class="description">描述文本</p>
<a href="https://example.com" id="link">点击我</a>
</div>
<!-- 数据展示区域 --> <div id="datav"> <div id="data"> 我是一段数据
<span class="highlight">高亮内容</span>
</div> <div id="additionalData" class="additional"> 额外的数据
</div> </div> </div>
<!-- 页脚区域 --> <footer> <p>© 2025 测试公司</p>
<p id="footer-note">这是页脚备注</p>
</footer></body>
</html>
"""
html_elment=etree.HTML(html_test)
print(html_elment)
在做好以上这些数据准备后,我们就可以来练习语法了
语法练习
选取所有div节点:
div=html_elment.xpath("//div")
选取所有div下的p节点:
p=html_elment.xpath("//div/p")
选取文档内第一个div节点:
first_div=html_elment.xpath("(//div)[1]")
选取所有class名为module的节点:
module=html_elment.xpath("//*[@class='moudle']")
选取所有id为data的节点:
id=html_elment.xpath("//*[@id='data']")
在打印执行了xpath的对象后,我们可以看到它仍然为一个elment对象,想要看到它的内容,我们可以使用text属性与tostring方法:
#获取所有div节点的内容and文本
div=html_elment.xpath("//div")
for index,d in enumerate(div):
#使用enumerate方法获取迭代索引迭代
if d.text is not None and d.text.strip():
#判空,需要使用strip方法去除空字符来判定空字符串
print(f"第{index}个元素的文本内容为:{d.text}")
print(f"第{index}个元素的内容为:\n{etree.tostring(d,encoding='unicode')}")
#中文需要使用unicode编码,不然无法正常显示
else:
print(f"第{index}个元素的文本内容为:空")
在获取到内容后,我们可能还需要将内容保存起来:
扩展:内容存储
导入csv模块:
import csv
创建csv并同时创建写入流/写入器:
with open("保存示例.csv",mode='w',encoding='utf-8',newline='') as file:
#创建csv写入流
write=csv.writer(file)
#写入表头
write.writerow(['序号','内容'])
for迭代写入csv:
for index,d in enumerate(div):
xh=index
nr=d.text
write.writerow([xh,nr])
完整代码:
import csv
#创建文件,写模式,编码为utf-8,
with open("保存示例.csv",mode='w',encoding='utf-8',newline='') as file:
#创建csv写入流
write=csv.writer(file)
#写入表头
write.writerow(['序号','内容'])
for index,d in enumerate(div):
xh=index
nr=d.text
write.writerow([xh,nr])
可以看到,保存的方法比较简单,只需要writer方法传入文件创建写入流,再通过wirterow方法写入表头与数据即可,在后面我们接触到pandas后,保存会更加简单快捷。
学习到这儿,应该能发现一个问题:获取到的这些节点内的数据杂七杂八的,我如何获取到指定阶段的数据呢?你可能会想到存储后使用excel来进行筛选,但如果数据量非常大的情况下,如何使用一个合适的工具来筛选它呢?

浙公网安备 33010602011771号