Skip to main content

xml

xml.etree.ElementTree 模块提供了轻量高效的 XML 解析与生成 API,是 Python 处理 XML 的首选工具。

xml.etree.ElementTree

解析 XML

import xml.etree.ElementTree as ET

# 从文件解析
tree = ET.parse('data.xml')
root = tree.getroot()

# 从字符串解析
xml_str = """<?xml version="1.0"?>
<bookstore>
<book category="编程">
<title>Python入门</title>
<author>张三</author>
<price>59.9</price>
</book>
<book category="文学">
<title>三体</title>
<author>刘慈欣</author>
<price>36.0</price>
</book>
</bookstore>
"""
root = ET.fromstring(xml_str)
print(root.tag) # bookstore
print(root.attrib) # {}

遍历与查找元素

import xml.etree.ElementTree as ET

root = ET.fromstring("""<bookstore>
<book category="编程">
<title>Python入门</title>
<author>张三</author>
<price>59.9</price>
</book>
<book category="文学">
<title>三体</title>
<author>刘慈欣</author>
<price>36.0</price>
</book>
</bookstore>""")

# 遍历直接子元素
for child in root:
print(child.tag, child.attrib)
# book {'category': '编程'}
# book {'category': '文学'}

# find:查找第一个匹配的子元素
first_title = root.find('.//title')
print(first_title.text) # Python入门

# findall:查找所有匹配的元素
for book in root.findall('book'):
title = book.find('title').text
price = book.find('price').text
category = book.get('category')
print(f"[{category}] {title} - ¥{price}")
# [编程] Python入门 - ¥59.9
# [文学] 三体 - ¥36.0

# iter:递归遍历所有后代中的指定标签
for author in root.iter('author'):
print(author.text)
# 张三
# 刘慈欣
find vs findall vs iter
  • find(path):返回第一个匹配的子元素(或 None
  • findall(path):返回所有匹配的子元素列表
  • iter(tag):递归遍历整棵树中所有匹配的元素

修改 XML

import xml.etree.ElementTree as ET

root = ET.fromstring("""<data>
<item id="1"><value>100</value></item>
<item id="2"><value>200</value></item>
<item id="3"><value>300</value></item>
</data>""")

# 修改文本内容
for item in root.findall('item'):
val = item.find('value')
val.text = str(int(val.text) * 2)

# 修改/添加属性
for item in root.findall('item'):
item.set('updated', 'yes')

# 删除元素(删除 id="3" 的项)
for item in root.findall('item'):
if item.get('id') == '3':
root.remove(item)

print(ET.tostring(root, encoding='unicode'))
# <data>
# <item id="1" updated="yes"><value>200</value></item>
# <item id="2" updated="yes"><value>400</value></item>
# </data>
tip

遍历时删除元素需小心——先用 findall() 收集目标元素,再逐一删除,避免在迭代中修改集合。

创建 XML

import xml.etree.ElementTree as ET

# 创建根元素
root = ET.Element('config')

# 添加子元素
db = ET.SubElement(root, 'database')
ET.SubElement(db, 'host').text = 'localhost'
ET.SubElement(db, 'port').text = '5432'
ET.SubElement(db, 'name').text = 'mydb'

server = ET.SubElement(root, 'server')
server.set('debug', 'true')
ET.SubElement(server, 'host').text = '0.0.0.0'
ET.SubElement(server, 'port').text = '8080'

# 格式化缩进(Python 3.9+)
ET.indent(root)

# 输出为字符串
xml_str = ET.tostring(root, encoding='unicode', xml_declaration=True)
print(xml_str)

# 写入文件
tree = ET.ElementTree(root)
tree.write('config.xml', encoding='utf-8', xml_declaration=True)

XPath 查询

ElementTree 支持简化版的 XPath 表达式,用于灵活查找元素。

import xml.etree.ElementTree as ET

root = ET.fromstring("""<store>
<department name="电子">
<product price="2999">手机</product>
<product price="5999">笔记本</product>
</department>
<department name="图书">
<product price="45">Python编程</product>
<product price="38">数据结构</product>
</department>
</store>""")

# 查找所有 product 元素(任意层级)
products = root.findall('.//product')
print(len(products)) # 4

# 带属性条件查找
electronics = root.findall(".//department[@name='电子']/product")
for p in electronics:
print(f"{p.text}: ¥{p.get('price')}")
# 手机: ¥2999
# 笔记本: ¥5999

# 查找所有 department 的第一个 product
firsts = root.findall('.//department/product[1]')
for p in firsts:
print(p.text) # 手机、Python编程
常用 XPath 语法
语法说明
tag直接子元素
*所有直接子元素
.当前元素
//所有层级的后代
..父元素
[@attrib]有该属性的元素
[@attrib='value']属性等于指定值
[tag]有指定子元素的元素
[position]按位置筛选(从 1 开始)

处理命名空间

import xml.etree.ElementTree as ET

xml_str = """<?xml version="1.0"?>
<root xmlns:app="http://example.com/app">
<app:user id="1">
<app:name>Alice</app:name>
<app:email>alice@example.com</app:email>
</app:user>
</root>"""

root = ET.fromstring(xml_str)

# 方式一:在路径中写完整 URI
user = root.find('{http://example.com/app}user')
print(user.find('{http://example.com/app}name').text) # Alice

# 方式二:定义命名空间映射(更简洁)
ns = {'app': 'http://example.com/app'}
user = root.find('app:user', ns)
name = user.find('app:name', ns)
email = user.find('app:email', ns)
print(f"{name.text} <{email.text}>") # Alice <alice@example.com>