浮头导航网

专注编程技术分享的开发者社区

Python的lxml库助力我们轻松解析XML文件

大家好,我是科雷! 我们在编写程序或者编写自动化用例时,经常会用到XML配置文件,而lxml作为性能卓越的解析库,凭借其高效、灵活的特性,成为众多开发者的首选工具。本文主要介绍如何使用lxml库来解析xml文件,帮助我们了解xml文件的结构和解析方式。

1.安装lxml库

通过pip命令安装:pip install lxml -i
https://mirrors.aliyun.com/pypi/simple/

2.XML解析函数

核心函数

  • etree.XML(text):解析XML文本,返回Element对象。
  • etree.parse(file_or_url):从文件或URL解析XML文档。

案例:解析XML内容

from lxml import etree

xml = """
<config>
    <server>
        <host>localhost</host>
        <port>8080</port>
    </server>
    <database>
        <name>mydb</name>
        <user>root</user>
        <password>secret</password>
    </database>
</config>
"""

# 解析XML
root = etree.XML(xml)
print(root.tag)
#输出: config

# 获取config的子元素
for child in root:
    print(child.tag)  
# 输出:server database

3.XPath选择器

XPath是XML路径语言,用于在XML文档中定位元素。在lxml中,通过Element.xpath()方法使用 XPath表达式,返回匹配的元素对象列表,匹配不到返回空列表

基础语法:节点选择

表达式

功能描述

案例(基于上述 XML)

结果

tag

选择当前节点下的子元素

root.xpath('server')

匹配<server>元素(1 个)

//tag

选择文档中所有的tag元素(不管层级)

root.xpath('//port')

匹配<port>元素(1 个)

/

从根节点开始选择

root.xpath('/config/server')

从根节点匹配<server>元素

.

选择当前节点

server.xpath('./host')

从<server>节点匹配子元素<host>

..

选择当前节点的父节点

host.xpath('..')

匹配<host>的父节点<server>

案例:基础节点选择

# 选择所有server元素
servers = root.xpath('//server')
print(len(servers))  # 输出:1

# 选择server的直接子元素host
hosts = root.xpath('//server/host')
print(hosts[0].text)  # 输出:localhost

# 从根节点选择database
databases = root.xpath('/config/database')
print(databases[0].tag)  # 输出:database

属性过滤:精确匹配元素

通过[@属性名=值]过滤带特定属性的元素,支持多条件组合。

表达式

功能描述

案例(基于上述XML)

结果

tag[@attr]

选择带attr属性的tag元素

root.xpath('server[@id]')

匹配带id属性的<server>

tag[@attr='value']

选择attr属性为value的tag元素

root.xpath('server[@id="1"]')

匹配id="1"的<server>

tag[@attr1][@attr2]

多属性过滤(同时满足)

root.xpath('port[@protocol][@debug]')

匹配同时带protocol和debug的<port>

tag[@attr!='value']

排除属性值为value的元素

root.xpath('server[@id!="2"]')

排除id="2"的<server>

案例:属性过滤

xml = """
<config>
    <server id="1">
        <host>localhost</host>
        <port protocol="tcp">8080</port>
    </server>
    <server id="2">
        <host>localhost</host>
        <port protocol="udp">8081</port>
    </server>
    <database>
        <name>mydb</name>
        <user>root</user>
        <password>secret</password>
    </database>
</config>
"""

# 选择带protocol属性的port元素
ports = root.xpath('//port[@protocol]')
print(ports[0].text)  # 输出:8080

# 选择protocol="tcp"的port元素
tcp_ports = root.xpath('//port[@protocol="udp"]')
print(tcp_ports[0].text)  # 输出:8081

# 多条件过滤(假设port有debug属性)
# debug_ports = root.xpath('//port[@protocol="tcp"][@debug="true"]')

位置过滤:按索引选择

通过索引或逻辑条件选择特定位置的元素(XPath索引从1开始)。

表达式

功能描述

案例(假设有多个 server)

结果

tag[n]

选择第n个tag元素

root.xpath('//server[1]')

第一个<server>

tag[last()]

选择最后一个tag元素

root.xpath('//server[last()]')

最后一个<server>

tag[position()<n]

选择前n-1个tag元素

root.xpath('//server[position()<3]')

前2个<server>

tag[last()-1]

选择倒数第二个tag元素

root.xpath('//server[last()-1]')

倒数第二个<server>

案例:位置过滤

# 假设XML中有多个server:<server id="1">、<server id="2">
# 选择第一个server
first_server = root.xpath('//server[1]')
print(first_server[0].get('id'))  # 输出:1

# 选择最后一个server
last_server = root.xpath('//server[last()]')
print(last_server[0].get('id'))  # 输出:2

文本过滤:按内容匹配

通过text()函数过滤元素文本内容,支持模糊匹配。

表达式

功能描述

案例(基于上述 XML)

结果

tag[text()='value']

文本完全匹配value

root.xpath('//host[text()="localhost"]')

匹配文本为localhost的<host>

tag[contains(text(), 'sub')]

文本包含sub子串

root.xpath('//password[contains(text(), "sec")]')

匹配文本含sec的<password>

tag[starts-with(text(), 'pre')]

文本以pre开头

root.xpath('//name[starts-with(text(), "my")]')

匹配文本以my开头的<name>

案例:文本过滤

# 匹配host文本为localhost的元素
local_hosts = root.xpath('//host[text()="localhost"]')
print(local_hosts[0].text)  # 输出:localhost

# 匹配name中包含"db"的元素
db_names = root.xpath('//name[contains(text(), "db")]')
print(db_names[0].text)  # 输出:mydb

获取属性值与文本

通过@属性名提取属性值,通过text()提取文本内容。

表达式

功能描述

案例(基于上述 XML)

结果

tag/@attr

提取tag元素的attr属性值

root.xpath('//server/@id')

输出:['1']

tag/text()

提取tag元素的文本内容

root.xpath('//port/text()')

输出:['8080']

//@attr

提取文档中所有attr属性值

root.xpath('//@protocol')

输出:['tcp']

案例:提取属性与文本

# 提取所有server的id属性
server_ids = root.xpath('//server/@id')
print(server_ids)  # 输出:['1', '2']

# 提取所有port的文本内容
port_values = root.xpath('//port/text()')
print(port_values)  # 输出:['8080', '8081']

# 提取所有protocol属性值
protocols = root.xpath('//@protocol')
print(protocols)  # 输出:['tcp', 'udp']

组合条件与运算符

支持and/or逻辑运算,以及>/<等比较运算。

表达式

功能描述

案例(假设 port 有数值)

结果

tag[@a='x' and @b='y']

多属性同时满足

root.xpath('port[@protocol="tcp" and @port>8000]')

匹配协议为tcp且端口 > 8000 的 port

tag[text()!='x' or @a='y']

文本不匹配或属性匹配

root.xpath('host[text()!="localhost" or @active="true"]')

匹配非localhost或active=true的host

tag[@num>100]

数值比较(>、<、>=、<=)

root.xpath('//port[@num>8080]')

匹配num>8080的port

案例:组合条件

# 选择protocol为tcp且端口文本为8080的port
tcp_8080 = root.xpath('//port[@protocol="tcp" and text()="8080"]')
print(len(tcp_8080))  # 输出:1

# 选择id为1或包含host子元素的server
servers = root.xpath('//server[@id="1" or host]')
print(len(servers))  # 输出:1(满足id="1")

4.Element对象详解

etree.XML(text)返回的Element对象是lxml库的核心数据结构,代表XML文档中的一个节点。它提供了丰富的属性和方法,用于操作XML数据。

Element对象的核心属性

属性

描述

示例

tag

元素的标签名

root.tag → 'config'

text

元素的文本内容

host.text → 'localhost'

tail

元素结束标签后的文本

通常为空白或注释

attrib

元素的属性字典

server.attrib → {'id': '1'}

sourceline

元素在原XML中的行号

host.sourceline → 3

Element 对象的常用方法

1. 获取元素信息

# 获取属性值
print(root.get('id'))  # 输出:None(无此属性)

# 获取父元素
server = port.getparent()
print(server.tag)  # 输出:server

# 获取子元素列表
children = server.getchildren()  # 等价于 list(server)
for child in children:
    print(child.tag)  # 输出:host port

2. 修改元素内容

# 修改文本内容
host = root.xpath('//host')[0]
host.text = '127.0.0.1'

# 添加/修改属性
server.set('enabled', 'true')
server.set('version', '2.0')

# 删除属性
del server.attrib['version']

# 添加子元素
new_child = etree.SubElement(server, 'timeout')
new_child.text = '300'

3. 元素遍历与选择

# 遍历某个元素的子元素
for child in server:
    print(child.tag)

# 递归遍历某个元素的所有子元素
for element in server.iter():
    print(element.tag)

4.创建与删除元素

# 创建新元素
new_db = etree.Element('database')
etree.SubElement(new_db, 'name').text = 'newdb'
etree.SubElement(new_db, 'user').text = 'admin'

# 添加到根元素
root.append(new_db)

# 删除元素
root.remove(new_db)

ElementPath:简化选择语法

Element 对象支持直接使用 XPath-like 语法进行选择

# 等价于 root.xpath('//database/name/text()')
db_names = root.findall('.//database/name')
for name in db_names:
    print(name.text)

# 获取单个元素
first_db = root.find('.//database')
print(first_db.xpath('user/text()')[0])

五、以事件驱动的方式逐段解析XML

使用etree.iterparse以事件驱动的方式逐段解析XML,无需一次性加载整个文档到内存,适合处理GB级大型文件。

基本语法

from lxml import etree

for event, element in etree.iterparse(source, events=('start', 'end'), 
                                      tag='目标标签'):
    # 处理元素
    pass

关键参数

参数

说明

source

可传入文件路径、文件对象或字节流(如open('large.xml', 'rb'))

events

监听的事件列表,常用('start', 'end'):
- 'start':元素开始标签被解析时触发
- 'end':元素结束标签被解析时触发(推荐,此时元素内容已完整)

tag

只监听指定标签的元素(可选,指定后可提升效率)

案例:解析大型XML中的指定元素

假设有一个包含 100 万条item记录的large.xml:

<data>
    <item id="1">
        <name>产品A</name>
        <price>99.9</price>
    </item>
    <!-- 更多item... -->
</data>

使用iterparse提取所有item的name和price:

from lxml import etree

# 打开大型XML文件(使用二进制模式)
with open('large.xml', 'rb') as f:
    # 只监听item元素的end事件(内容已完整)
    for event, elem in etree.iterparse(f, events=('end',), tag='item'):
        # 提取数据
        item_id = elem.get('id')
        name = elem.xpath('./name/text()')[0]
        price = elem.xpath('./price/text()')[0]
        print(f"ID: {item_id}, 名称: {name}, 价格: {price}")
        
        # 关键:清除已处理元素,释放内存
        elem.clear()
        # 移除父节点引用(彻底释放)
        while elem.getprevious() is not None:
            del elem.getparent()[0]

总结:掌握上述语法能让你快速定位和提取数据。建议结合实际 XML 结构多练习,熟练后可大幅提升解析效率!

关注我的头条号,获取更多Python高效技巧!
最近的5篇文章:

保姆级教程:使用Python实现OCR图片识别文字(环境搭建和使用)

使用Python实现Markdown文件与HTML文件互相转换,超简单!!!

Python的argparse库:解析命令行参数的好帮手

Python中PyPDF2库全解析:轻松玩转PDF文件处理

Python中schedule库:轻松实现任务定时自动化

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言