Appearance
Python XML 解析
XML(可扩展标记语言)是一种用于存储和传输数据的标记语言,它具有自我描述性和平台无关性。Python 提供了多种库来解析和处理 XML 数据,本章节将详细介绍这些库的使用方法。
XML 基础
XML 文档结构
XML 文档由以下部分组成:
- 声明:XML 文档的第一行,指定 XML 版本和编码
- 根元素:XML 文档的顶级元素
- 子元素:根元素内的元素
- 属性:元素的属性
- 文本:元素内的文本内容
XML 示例
xml
<?xml version="1.0" encoding="UTF-8"?>
<library>
<book id="1">
<title>Python Programming</title>
<author>John Doe</author>
<year>2023</year>
<price>29.99</price>
</book>
<book id="2">
<title>Advanced Python</title>
<author>Jane Smith</author>
<year>2024</year>
<price>39.99</price>
</book>
</library>Python XML 解析库
Python 中常用的 XML 解析库包括:
- xml.etree.ElementTree:Python 标准库,提供了简单高效的 XML 解析功能
- xml.dom.minidom:Python 标准库,提供了 DOM(文档对象模型)风格的 XML 解析
- lxml:第三方库,提供了更强大和灵活的 XML 解析功能
本章节将详细介绍这些库的使用方法。
使用 xml.etree.ElementTree
xml.etree.ElementTree 是 Python 标准库中推荐的 XML 解析库,它提供了简单高效的 XML 解析功能。
解析 XML 文件
python
import xml.etree.ElementTree as ET
# 解析 XML 文件
tree = ET.parse('books.xml')
root = tree.getroot()
# 打印根元素
print(f"根元素: {root.tag}")
# 遍历子元素
for book in root.findall('book'):
# 获取属性
book_id = book.get('id')
# 获取子元素文本
title = book.find('title').text
author = book.find('author').text
year = book.find('year').text
price = book.find('price').text
print(f"\n书籍 ID: {book_id}")
print(f"标题: {title}")
print(f"作者: {author}")
print(f"年份: {year}")
print(f"价格: {price}")解析 XML 字符串
python
import xml.etree.ElementTree as ET
# XML 字符串
xml_string = '''
<?xml version="1.0" encoding="UTF-8"?>
<library>
<book id="1">
<title>Python Programming</title>
<author>John Doe</author>
<year>2023</year>
<price>29.99</price>
</book>
<book id="2">
<title>Advanced Python</title>
<author>Jane Smith</author>
<year>2024</year>
<price>39.99</price>
</book>
</library>
'''
# 解析 XML 字符串
root = ET.fromstring(xml_string)
# 打印根元素
print(f"根元素: {root.tag}")
# 遍历子元素
for book in root.findall('book'):
# 获取属性
book_id = book.get('id')
# 获取子元素文本
title = book.find('title').text
author = book.find('author').text
year = book.find('year').text
price = book.find('price').text
print(f"\n书籍 ID: {book_id}")
print(f"标题: {title}")
print(f"作者: {author}")
print(f"年份: {year}")
print(f"价格: {price}")查找元素
python
import xml.etree.ElementTree as ET
# 解析 XML 文件
tree = ET.parse('books.xml')
root = tree.getroot()
# 查找所有 book 元素
print("所有书籍:")
for book in root.findall('book'):
title = book.find('title').text
print(f"- {title}")
# 查找特定属性的 book 元素
print("\nID 为 1 的书籍:")
book = root.find("book[@id='1']")
if book:
title = book.find('title').text
author = book.find('author').text
print(f"标题: {title}")
print(f"作者: {author}")
# 查找价格大于 30 的书籍
print("\n价格大于 30 的书籍:")
for book in root.findall('book'):
price = float(book.find('price').text)
if price > 30:
title = book.find('title').text
print(f"- {title} (价格: {price})")
# 递归查找所有 title 元素
print("\n所有标题:")
for title in root.iter('title'):
print(f"- {title.text}")修改 XML
python
import xml.etree.ElementTree as ET
# 解析 XML 文件
tree = ET.parse('books.xml')
root = tree.getroot()
# 修改元素文本
print("修改前:")
for book in root.findall('book'):
price = book.find('price')
print(f"{book.find('title').text}: {price.text}")
# 增加所有书籍的价格
for book in root.findall('book'):
price = book.find('price')
current_price = float(price.text)
new_price = current_price * 1.1 # 增加 10%
price.text = str(round(new_price, 2))
# 添加新元素
new_book = ET.SubElement(root, 'book', id='3')
ET.SubElement(new_book, 'title').text = 'Python for Beginners'
ET.SubElement(new_book, 'author').text = 'Bob Brown'
ET.SubElement(new_book, 'year').text = '2022'
ET.SubElement(new_book, 'price').text = '19.99'
# 删除元素
book_to_remove = root.find("book[@id='1']")
if book_to_remove:
root.remove(book_to_remove)
print("\n已删除 ID 为 1 的书籍")
# 保存修改后的 XML
output_file = 'modified_books.xml'
tree.write(output_file, encoding='UTF-8', xml_declaration=True)
print(f"\n修改后的 XML 已保存到 {output_file}")
# 打印修改后的内容
print("\n修改后:")
tree = ET.parse(output_file)
root = tree.getroot()
for book in root.findall('book'):
title = book.find('title').text
price = book.find('price').text
print(f"{title}: {price}")创建 XML
python
import xml.etree.ElementTree as ET
# 创建根元素
root = ET.Element('library')
# 创建子元素
book1 = ET.SubElement(root, 'book', id='1')
ET.SubElement(book1, 'title').text = 'Python Programming'
ET.SubElement(book1, 'author').text = 'John Doe'
ET.SubElement(book1, 'year').text = '2023'
ET.SubElement(book1, 'price').text = '29.99'
book2 = ET.SubElement(root, 'book', id='2')
ET.SubElement(book2, 'title').text = 'Advanced Python'
ET.SubElement(book2, 'author').text = 'Jane Smith'
ET.SubElement(book2, 'year').text = '2024'
ET.SubElement(book2, 'price').text = '39.99'
# 创建 ElementTree 对象
tree = ET.ElementTree(root)
# 保存到文件
output_file = 'new_books.xml'
tree.write(output_file, encoding='UTF-8', xml_declaration=True)
print(f"XML 文件已创建: {output_file}")
# 打印创建的 XML
print("\n创建的 XML 内容:")
with open(output_file, 'r', encoding='UTF-8') as f:
print(f.read())使用 xml.dom.minidom
xml.dom.minidom 是 Python 标准库中的另一个 XML 解析库,它提供了 DOM(文档对象模型)风格的 XML 解析。
解析 XML 文件
python
from xml.dom.minidom import parse
# 解析 XML 文件
doc = parse('books.xml')
# 获取根元素
root = doc.documentElement
print(f"根元素: {root.tagName}")
# 获取所有 book 元素
books = root.getElementsByTagName('book')
print(f"\n共有 {books.length} 本书籍")
# 遍历 book 元素
for i, book in enumerate(books):
# 获取属性
book_id = book.getAttribute('id')
print(f"\n书籍 {i+1} ID: {book_id}")
# 获取子元素
title = book.getElementsByTagName('title')[0].firstChild.nodeValue
author = book.getElementsByTagName('author')[0].firstChild.nodeValue
year = book.getElementsByTagName('year')[0].firstChild.nodeValue
price = book.getElementsByTagName('price')[0].firstChild.nodeValue
print(f"标题: {title}")
print(f"作者: {author}")
print(f"年份: {year}")
print(f"价格: {price}")解析 XML 字符串
python
from xml.dom.minidom import parseString
# XML 字符串
xml_string = '''
<?xml version="1.0" encoding="UTF-8"?>
<library>
<book id="1">
<title>Python Programming</title>
<author>John Doe</author>
<year>2023</year>
<price>29.99</price>
</book>
<book id="2">
<title>Advanced Python</title>
<author>Jane Smith</author>
<year>2024</year>
<price>39.99</price>
</book>
</library>
'''
# 解析 XML 字符串
doc = parseString(xml_string)
# 获取根元素
root = doc.documentElement
print(f"根元素: {root.tagName}")
# 获取所有 book 元素
books = root.getElementsByTagName('book')
print(f"\n共有 {books.length} 本书籍")
# 遍历 book 元素
for i, book in enumerate(books):
# 获取属性
book_id = book.getAttribute('id')
print(f"\n书籍 {i+1} ID: {book_id}")
# 获取子元素
title = book.getElementsByTagName('title')[0].firstChild.nodeValue
author = book.getElementsByTagName('author')[0].firstChild.nodeValue
year = book.getElementsByTagName('year')[0].firstChild.nodeValue
price = book.getElementsByTagName('price')[0].firstChild.nodeValue
print(f"标题: {title}")
print(f"作者: {author}")
print(f"年份: {year}")
print(f"价格: {price}")创建和修改 XML
python
from xml.dom.minidom import Document
# 创建文档
doc = Document()
# 创建根元素
library = doc.createElement('library')
doc.appendChild(library)
# 创建第一个 book 元素
book1 = doc.createElement('book')
book1.setAttribute('id', '1')
library.appendChild(book1)
# 添加子元素
title1 = doc.createElement('title')
title1.appendChild(doc.createTextNode('Python Programming'))
book1.appendChild(title1)
author1 = doc.createElement('author')
author1.appendChild(doc.createTextNode('John Doe'))
book1.appendChild(author1)
year1 = doc.createElement('year')
year1.appendChild(doc.createTextNode('2023'))
book1.appendChild(year1)
price1 = doc.createElement('price')
price1.appendChild(doc.createTextNode('29.99'))
book1.appendChild(price1)
# 创建第二个 book 元素
book2 = doc.createElement('book')
book2.setAttribute('id', '2')
library.appendChild(book2)
title2 = doc.createElement('title')
title2.appendChild(doc.createTextNode('Advanced Python'))
book2.appendChild(title2)
author2 = doc.createElement('author')
author2.appendChild(doc.createTextNode('Jane Smith'))
book2.appendChild(author2)
year2 = doc.createElement('year')
year2.appendChild(doc.createTextNode('2024'))
book2.appendChild(year2)
price2 = doc.createElement('price')
price2.appendChild(doc.createTextNode('39.99'))
book2.appendChild(price2)
# 保存到文件
output_file = 'dom_books.xml'
with open(output_file, 'w', encoding='UTF-8') as f:
f.write(doc.toprettyxml(indent=' '))
print(f"XML 文件已创建: {output_file}")
# 打印创建的 XML
print("\n创建的 XML 内容:")
with open(output_file, 'r', encoding='UTF-8') as f:
print(f.read())使用 lxml
lxml 是一个第三方库,提供了更强大和灵活的 XML 解析功能,它基于 C 语言的 libxml2 和 libxslt 库,性能比标准库更好。
安装 lxml
bash
pip install lxml解析 XML 文件
python
from lxml import etree
# 解析 XML 文件
tree = etree.parse('books.xml')
root = tree.getroot()
# 打印根元素
print(f"根元素: {root.tag}")
# 遍历子元素
for book in root.findall('book'):
# 获取属性
book_id = book.get('id')
# 获取子元素文本
title = book.find('title').text
author = book.find('author').text
year = book.find('year').text
price = book.find('price').text
print(f"\n书籍 ID: {book_id}")
print(f"标题: {title}")
print(f"作者: {author}")
print(f"年份: {year}")
print(f"价格: {price}")使用 XPath 查找元素
python
from lxml import etree
# 解析 XML 文件
tree = etree.parse('books.xml')
root = tree.getroot()
# 使用 XPath 查找所有 book 元素
print("所有书籍:")
books = root.xpath('//book')
for book in books:
title = book.xpath('./title/text()')[0]
print(f"- {title}")
# 使用 XPath 查找特定属性的 book 元素
print("\nID 为 1 的书籍:")
book = root.xpath('//book[@id="1"]')[0]
title = book.xpath('./title/text()')[0]
author = book.xpath('./author/text()')[0]
print(f"标题: {title}")
print(f"作者: {author}")
# 使用 XPath 查找价格大于 30 的书籍
print("\n价格大于 30 的书籍:")
books = root.xpath('//book[price > 30]')
for book in books:
title = book.xpath('./title/text()')[0]
price = book.xpath('./price/text()')[0]
print(f"- {title} (价格: {price})")
# 使用 XPath 查找所有标题
print("\n所有标题:")
titles = root.xpath('//title/text()')
for title in titles:
print(f"- {title}")
# 使用 XPath 查找第二本书的作者
print("\n第二本书的作者:")
author = root.xpath('//book[2]/author/text()')[0]
print(f"- {author}")修改 XML
python
from lxml import etree
# 解析 XML 文件
tree = etree.parse('books.xml')
root = tree.getroot()
# 修改元素文本
print("修改前:")
for book in root.findall('book'):
price = book.find('price')
print(f"{book.find('title').text}: {price.text}")
# 增加所有书籍的价格
for book in root.findall('book'):
price = book.find('price')
current_price = float(price.text)
new_price = current_price * 1.1 # 增加 10%
price.text = str(round(new_price, 2))
# 添加新元素
new_book = etree.SubElement(root, 'book', id='3')
etree.SubElement(new_book, 'title').text = 'Python for Beginners'
etree.SubElement(new_book, 'author').text = 'Bob Brown'
etree.SubElement(new_book, 'year').text = '2022'
etree.SubElement(new_book, 'price').text = '19.99'
# 删除元素
book_to_remove = root.find("book[@id='1']")
if book_to_remove is not None:
root.remove(book_to_remove)
print("\n已删除 ID 为 1 的书籍")
# 保存修改后的 XML
output_file = 'lxml_modified_books.xml'
tree.write(output_file, encoding='UTF-8', xml_declaration=True, pretty_print=True)
print(f"\n修改后的 XML 已保存到 {output_file}")
# 打印修改后的内容
print("\n修改后:")
tree = etree.parse(output_file)
root = tree.getroot()
for book in root.findall('book'):
title = book.find('title').text
price = book.find('price').text
print(f"{title}: {price}")创建 XML
python
from lxml import etree
# 创建根元素
root = etree.Element('library')
# 创建子元素
book1 = etree.SubElement(root, 'book', id='1')
etree.SubElement(book1, 'title').text = 'Python Programming'
etree.SubElement(book1, 'author').text = 'John Doe'
etree.SubElement(book1, 'year').text = '2023'
etree.SubElement(book1, 'price').text = '29.99'
book2 = etree.SubElement(root, 'book', id='2')
etree.SubElement(book2, 'title').text = 'Advanced Python'
etree.SubElement(book2, 'author').text = 'Jane Smith'
etree.SubElement(book2, 'year').text = '2024'
etree.SubElement(book2, 'price').text = '39.99'
# 创建 ElementTree 对象
tree = etree.ElementTree(root)
# 保存到文件
output_file = 'lxml_new_books.xml'
tree.write(output_file, encoding='UTF-8', xml_declaration=True, pretty_print=True)
print(f"XML 文件已创建: {output_file}")
# 打印创建的 XML
print("\n创建的 XML 内容:")
with open(output_file, 'r', encoding='UTF-8') as f:
print(f.read())XML 解析的实际应用
示例 1:解析 RSS 订阅
python
import xml.etree.ElementTree as ET
import requests
# 获取 RSS 订阅内容
url = "https://www.python.org/blogs/feed/"
response = requests.get(url)
xml_content = response.content
# 解析 XML
root = ET.fromstring(xml_content)
# 打印频道信息
channel = root.find('channel')
title = channel.find('title').text
description = channel.find('description').text
link = channel.find('link').text
print(f"频道标题: {title}")
print(f"频道描述: {description}")
print(f"频道链接: {link}")
# 打印最新的 5 篇文章
print("\n最新的 5 篇文章:")
items = channel.findall('item')[:5]
for i, item in enumerate(items):
item_title = item.find('title').text
item_link = item.find('link').text
item_pubdate = item.find('pubDate').text
print(f"\n{i+1}. {item_title}")
print(f"链接: {item_link}")
print(f"发布日期: {item_pubdate}")示例 2:生成 XML 配置文件
python
import xml.etree.ElementTree as ET
# 创建配置文件
root = ET.Element('configuration')
# 添加数据库配置
database = ET.SubElement(root, 'database')
ET.SubElement(database, 'host').text = 'localhost'
ET.SubElement(database, 'port').text = '3306'
ET.SubElement(database, 'user').text = 'root'
ET.SubElement(database, 'password').text = 'password'
ET.SubElement(database, 'name').text = 'test_db'
# 添加服务器配置
server = ET.SubElement(root, 'server')
ET.SubElement(server, 'host').text = '0.0.0.0'
ET.SubElement(server, 'port').text = '8080'
ET.SubElement(server, 'debug').text = 'true'
# 添加日志配置
logging = ET.SubElement(root, 'logging')
ET.SubElement(logging, 'level').text = 'INFO'
ET.SubElement(logging, 'file').text = 'app.log'
ET.SubElement(logging, 'format').text = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
# 创建 ElementTree 对象
tree = ET.ElementTree(root)
# 保存到文件
output_file = 'config.xml'
tree.write(output_file, encoding='UTF-8', xml_declaration=True)
print(f"配置文件已创建: {output_file}")
# 打印创建的配置文件
print("\n创建的配置文件内容:")
with open(output_file, 'r', encoding='UTF-8') as f:
print(f.read())示例 3:解析和处理大型 XML 文件
对于大型 XML 文件,可以使用迭代器来减少内存使用:
python
import xml.etree.ElementTree as ET
# 创建 XML 文件
large_xml = '''
<?xml version="1.0" encoding="UTF-8"?>
<data>
'''
# 添加 1000 个元素
for i in range(1000):
large_xml += f' <item id="{i+1}"><name>Item {i+1}</name><value>{i+1}</value></item>\n'
large_xml += '''
</data>
'''
# 保存到文件
with open('large.xml', 'w', encoding='UTF-8') as f:
f.write(large_xml)
print("大型 XML 文件已创建: large.xml")
# 使用迭代器解析大型 XML 文件
print("\n解析大型 XML 文件:")
count = 0
sum_value = 0
# 使用 iterparse 迭代解析
for event, elem in ET.iterparse('large.xml', events=('start', 'end')):
if event == 'end' and elem.tag == 'item':
# 获取元素属性和文本
item_id = elem.get('id')
name = elem.find('name').text
value = int(elem.find('value').text)
# 处理数据
count += 1
sum_value += value
# 每 100 个元素打印一次
if count % 100 == 0:
print(f"已处理 {count} 个元素,当前总和: {sum_value}")
# 清除元素,释放内存
elem.clear()
print(f"\n解析完成,共处理 {count} 个元素,总和: {sum_value}")XML 解析的最佳实践
1. 选择合适的解析库
- 小型 XML 文件:使用
xml.etree.ElementTree即可 - 大型 XML 文件:使用
xml.etree.ElementTree.iterparse()或lxml.etree.iterparse() - 需要 XPath 支持:使用
lxml - 需要 DOM 接口:使用
xml.dom.minidom
2. 错误处理
- 捕获解析错误:使用 try-except 捕获 XML 解析错误
- 验证 XML 格式:使用 XML Schema 或 DTD 验证 XML 格式
- 处理命名空间:注意处理 XML 命名空间
3. 性能优化
- 使用迭代器:对于大型 XML 文件,使用迭代器解析
- 清除元素:使用 iterparse 时,及时清除元素释放内存
- 使用 lxml:对于性能要求高的场景,使用 lxml 库
4. 安全性
- 避免 XML 注入:处理用户输入时,避免 XML 注入攻击
- 限制解析大小:限制 XML 文件的大小,防止拒绝服务攻击
- 使用安全的解析模式:使用
resolve_entities=False避免实体扩展攻击
5. 代码可读性
- 使用有意义的变量名:提高代码可读性
- 添加注释:解释复杂的 XML 结构和解析逻辑
- 模块化:将 XML 解析逻辑封装到函数或类中
总结
本章节介绍了 Python 中的 XML 解析,包括:
- XML 基础:XML 文档结构和示例
- Python XML 解析库:xml.etree.ElementTree、xml.dom.minidom、lxml
- xml.etree.ElementTree:解析、查找、修改、创建 XML
- xml.dom.minidom:DOM 风格的 XML 解析
- lxml:更强大的 XML 解析库,支持 XPath
- 实际应用示例:解析 RSS 订阅、生成 XML 配置文件、处理大型 XML 文件
- 最佳实践:选择合适的解析库、错误处理、性能优化、安全性、代码可读性
掌握 XML 解析技术,对于处理配置文件、数据交换、Web 服务等场景非常重要。通过合理选择和使用 XML 解析库,可以高效地处理各种 XML 数据。