Python 学习第 32 天。
之前,我们具体讲解了 HTML 文档,与其相似的还有 XML 文档,而 xpath 就是一种用于解析 XML 路径的语言,本质就是识别 XML 中各节点(类似于 HTML 中的各标签)。
**补充:**HTML 与 XML 一样为树形结构,xpath 也可以解析 HTML 文档。
一、XML
XML(可扩展标记语言),是用于数据描述、存储和传输的标记语言,核心功能在于存储结构性数据。它由声明、根节点、子节点、内容、属性、注释 组成,总体分为 "文档声明 + 主体树结构"。
1. 声明示例:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
(1) <?xml?> 为文档声明,属于处理指令,必须写在文档第一行;
(2) version="1.0" 将文档的版本定义为 1.0 (全球通用的规范版本);
(3) encoding="UTF-8" 将文档的字符编码格式定义为 utf-8;
(4) standalone="yes" 表示文档依赖外部文件。
2. 节点: 由 字母、数字、下划线(_)、连字符(-)、英文点(.) 构成,可根据自身需要按照见名知义的原则定义,但开头必须是 字母、下划线 ,中间不能用 空格、中文字符、特殊符号连接,严格区分大小写(同一个英文字母,大小写不同会被识别为不同的节点)。
**(1) 根节点:**每个文档只有一个,所有主体内容都包含在根节点内。
**(2) 子节点:**嵌套在根节点内的子节点,可以有多个。
4. 内容: 放在节点之间需要存储的信息,格式:<节点名>需要存储的信息</节点名>。
**5. 属性:**写在开始标签内部,和节点一样,属性名可以根据自身需要见名知义地定义,格式:<节点名 属性1="属性值1" 属性2="属性值2" ...... 属性n="属性值n">
6. 注释: 常用于说明该节点作用,格式:<!-- 注释内容 -->。
二、xpath
xpath 是一种查询语言,本质上就是在树形结构的文档中,按照节点查找内容。这部分我们介绍它的语法格式,该语法格式与逻辑在 Python 中的 lxml 模块中可用 xpath() 函数直接套用。
1. 路径
**(1) 绝对路径:**以 / 开头,从根节点开始,每个 / 后边跟的是下一层的直接节点,// 则表示所有后代节点(既有子代,也有孙代、曾孙代、......)
(2) 相对路径: 不以 / 开头,./待查找节点名 指的是当前节点下的待查找节点,../待查找节点名 指的是当前节点的父节点下的待查找节点。
2. 符号 / 函数
与路径结合使用。
(1) *: 表示 "通用",/* 就是查找某节点下的所有子节点(直接的节点,不包含孙代、曾孙代、......)
(2) @: 表示 "调用",@属性名 就是查找某标签下的某属性,**@***就是查找某标签下的所有属性
**(3) \[\]:**跟在某标签之后,内涵属性名 / 属性名 = "属性值" / 索引 / 结合函数、逻辑运算符、@调用属性,用于限定对象
**(4) ():**表示 "分组"
(5) text(): 用于显示某节点对应的文本,格式:/节点名/text()
**(6) node():**用于显示任意类型的节点(元素、文本、注释等)
**(7) comment():**用于显示注释节点
**(8) last():**最后一个节点
**(9) position() > n:**第 n-1 个节点,n 要具体到数字
**(10) contains(@属性名, '属性值'):**包含某属性的节点
**(11) start-with(@属性名, '待识别字段'):**某属性值以待识别字段开头的节点
**(12) and:**逻辑与,两边条件都满足
**(13) or:**逻辑或,两边条件满足一个
**(14) not():**逻辑非,与条件相反
三、lxml 模块
以以下 XML 文档为例:
xml = """<?xml version = "1.0" encoding = "utf-8"?>
<library>
<info name="城东图书馆" address="文明路88号" tel="020-12345678">
<total_books>20000</total_books>
</info>
<books>
<book id="B001" type="文学">
<book_name>朝花夕拾</book_name>
<author>鲁迅</author>
<publish>人民文学出版社</publish>
<stock>15</stock>
</book>
<book id="B002" type="科技">
<book_name>Python编程入门</book_name>
<author>张三</author>
<publish>机械工业出版社</publish>
<stock>28</stock>
</book>
<book id="B003" type="历史">
<book_name>明朝那些事儿</book_name>
<author>当年明月</author>
<publish>中国友谊出版公司</publish>
<stock>9</stock>
</book>
<book id="B004" type="文学">
<book_name>围城</book_name>
<author>钱钟书</author>
<publish>人民文学出版社</publish>
<stock>12</stock>
</book>
</books>
</library>
"""
**注意:**将 XML 文档在代码块中赋值给变量时,""" 与 <?xml ......?> 之间不要有换行或空格,否则会在后续用 etree.XML() 或 etree.HTML() 读取中报错。
1. 安装包
在代码块中输入:
pip install lxml
运行结果中,如果出现以下文字,说明之前已经安装过这个模块,可以直接导入使用:

如果是第一次安装,运行结果末尾部分则会出现 Successfully installed lxml - 5.3.0。5.3.0是版本号,可能会不同。
2. 导入包
在代码块中输入:
from lxml import etree
或:
from lxml import html
etree = html.etree
3. 解码
示例中的 XML 文档用 utf-8 编码,那么对应的用 utf-8 解码,将其转换为 bytes 格式。很关键,否则初始化对象时会报错。
xml_bytes = xml.encode("utf-8")
4. 初始化对象
格式:变量名 = etree.XML(解码后的对象名)
et = etree.XML(xml_bytes)
如果是 HTML 文档,就用 **etree.HTML()**函数。
5. 查找
(1) 格式:初始化后的对象名.xpath("绝对路径"),以列表的形式返回符合条件的元素所在的内容地址
et.xpath("/library/books/book")
运行结果1:

代码示例2:初始化后的对象名.xpath("绝对路径/text()"),以列表的形式返回符合条件的元素所在的内容文本
et.xpath("/library/books/book/book_name/text()")
运行结果2:'朝花夕拾', 'Python编程入门', '明朝那些事儿', '围城'
代码示例3:如果想取出列表中的单个元素,就结合列表的知识,用索引或遍历的方式将其取出
result_1 = et.xpath("/library/books/book/book_name/text()")
print("---索引---")
print(result_1[1])
print("---遍历---")
for i in result_1:
print(i)
运行结果3:

代码示例4:用相对路径写法,省略父节点,并查找指定属性的属性值,以列表的形式返回
et.xpath("//book/@id")
运行结果4:'B001', 'B002', 'B003', 'B004'
代码示例5:使用逻辑运算符筛选多个条件下的节点(没有匹配的节点时,返回一个空列表)
et.xpath("//book[@id = 'B002' and @type = '文学']/text()")
运行结果5:\[\]
代码示例6:使用逻辑运算符筛选多个条件下的节点,我们发现用 // 查询所有子节点的同时,返回的列表中出现了许多 "空值"
et.xpath("//book[@id = 'B002' and @type = '科技']//text()")
运行结果6:

代码示例7:为解决示例6中出现的情况,可以用strip()函数结合列表推导式处理
text_list = et.xpath("//book[@id='B002' and @type='科技']//text()")
clean_text = [t.strip() for t in text_list if t.strip()]
print(clean_text)
运行结果7:'Python编程入门', '张三', '机械工业出版社', '28'
用 lxml 模块中的 xpath() 解析函数查询 HTML 文档中的内容与 XML 文档同理
区别在于初始化对象时将 XML() 改为 HTML()