Python 中读取 XML 文件主要有以下几种方法,每种方法适用于不同的场景和需求。以下将使用一个名为 test.xml 的文件作为示例,其内容假设如下:
xml
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
</data>
1. 使用 xml.etree.ElementTree (标准库,推荐)
ElementTree 是 Python 标准库中最常用、最轻量级的 XML 解析模块,它提供了简单高效的 API 来解析和创建 XML 数据 。
python
import xml.etree.ElementTree as ET
# 方法一:从文件路径解析
tree = ET.parse('test.xml')
root = tree.getroot()
# 方法二:从XML字符串解析
# xml_string = open('test.xml').read()
# root = ET.fromstring(xml_string)
# 遍历所有元素
print("根节点标签:", root.tag)
for child in root:
print(f" 子节点标签: {child.tag}, 属性: {child.attrib}")
for subchild in child:
print(f" 孙节点标签: {subchild.tag}, 文本: {subchild.text}")
# 使用 find 和 findall 查找特定元素
for country in root.findall('country'):
name = country.get('name')
rank = country.find('rank').text
print(f"国家: {name}, 排名: {rank}")
# 使用 iter进行迭代查找
for neighbor in root.iter('neighbor'):
print(f"邻居: {neighbor.get('name')}, 方向: {neighbor.get('direction')}")
2. 使用 xml.dom.minidom (标准库,DOM 解析)
minidom 实现了 W3C DOM 接口,将整个 XML 文档加载到内存中形成一个树结构,适合需要随机访问或修改文档的场景 。
python
from xml.dom import minidom
# 解析 XML 文件
dom = minidom.parse('test.xml')
root = dom.documentElement
# 获取节点列表
countries = root.getElementsByTagName('country')
print(f"找到 {len(countries)} 个国家节点")
# 遍历节点并获取信息
for country in countries:
name = country.getAttribute('name')
print(f"国家名称: {name}")
# 获取子节点
rank_node = country.getElementsByTagName('rank')[0]
year_node = country.getElementsByTagName('year')[0]
print(f" 排名: {rank_node.firstChild.data}")
print(f" 年份: {year_node.firstChild.data}")
# 获取所有邻居
neighbors = country.getElementsByTagName('neighbor')
for neighbor in neighbors:
n_name = neighbor.getAttribute('name')
n_dir = neighbor.getAttribute('direction')
print(f" 邻居: {n_name}, 方向: {n_dir}")
3. 使用 BeautifulSoup (第三方库,功能强大)
BeautifulSoup 是一个流行的第三方库,最初用于解析 HTML,但同样能很好地处理 XML。它提供了非常灵活和"Pythonic"的查找方式 。
python
# 首先需要安装: pip install beautifulsoup4from bs4 import BeautifulSoup
# 读取文件内容
with open('test.xml', 'r', encoding='utf-8') as f:
xml_content = f.read()
# 创建 BeautifulSoup 对象,指定 XML 解析器
soup = BeautifulSoup(xml_content, 'xml') # 或使用 'lxml-xml'
# 查找元素
print("文档类型:", soup.find('?xml'))
print("根节点:", soup.data.name)
# 查找所有 country 标签
countries = soup.find_all('country')
for country in countries:
print(f"
国家: {country['name']}")
# 获取子标签的文本 rank = country.rank.text year = country.year.text
print(f" 排名: {rank}, 年份: {year}")
# 查找所有 neighbor 标签 neighbors = country.find_all('neighbor')
for neighbor in neighbors:
print(f" 邻居: {neighbor['name']}, 方向: {neighbor['direction']}")
# 使用 CSS 选择器for gdppc in soup.select('gdppc'):
print(f"人均GDP: {gdppc.text}")
4. 使用 lxml.etree (第三方库,高性能)
lxml 是一个功能丰富、性能优异的第三方库,它兼容 ElementTree API但提供了更多高级功能,如 XPath 支持 。
python
# 首先需要安装: pip install lxml
from lxml import etree
# 解析 XML 文件
tree = etree.parse('test.xml')
root = tree.getroot()
# 使用 ElementTree 兼容的方法
print("根节点:", root.tag)
for country in root:
print(f"国家属性: {country.attrib}")
# 使用 XPath 进行高级查询(lxml 特有优势)
# 查找所有 rank 元素
ranks = root.xpath('//rank')
print(f"
所有排名:")
for rank in ranks:
print(f" {rank.text}")
# 查找名为 Singapore 的国家的年份
singapore_year = root.xpath('//country[@name="Singapore"]/year/text()')
print(f"
Singapore 的年份: {singapore_year[0]}")
# 查找所有邻居的方向
directions = root.xpath('//neighbor/@direction')
print(f"所有邻居方向: {directions}")
5. 使用 xml.sax (标准库,流式解析)
SAX (Simple API for XML) 是一种基于事件的流式解析器,适合处理大型 XML 文件,因为它不需要将整个文档加载到内存中 。
python
import xml.sax
# 定义内容处理器class CountryHandler(xml.sax.ContentHandler):
def __init__(self):
self.current_data = ""
self.name = ""
self.rank = ""
def startElement(self, tag, attributes):
self.current_data = tag
if tag == "country":
self.name = attributes["name"]
print(f"
国家: {self.name}")
elif tag == "neighbor":
n_name = attributes["name"]
n_dir = attributes["direction"]
print(f" 邻居: {n_name}, 方向: {n_dir}")
def characters(self, content):
if self.current_data == "rank":
self.rank = content
elif self.current_data == "year":
self.year = content def endElement(self, tag):
if self.current_data == "rank":
print(f" 排名: {self.rank}")
elif self.current_data == "year":
print(f" 年份: {self.year}")
self.current_data = ""
# 创建解析器
parser = xml.sax.make_parser()
# 关闭命名空间处理
parser.setFeature(xml.sax.handler.feature_namespaces, 0)
# 设置内容处理器
handler = CountryHandler()
parser.setContentHandler(handler)
# 解析文件
parser.parse("test.xml")
方法对比与选择建议
| 方法 | 所属库 | 内存使用 | 速度 | 易用性 | 适用场景 | 主要特点 |
|---|---|---|---|---|---|---|
| xml.etree.ElementTree | 标准库 | 中等 | 快 | 高 | 大多数情况 | 轻量、Pythonic API、标准库无需安装 |
| xml.dom.minidom | 标准库 | 高 | 慢 | 中 | 小文件、需要完整 DOM 树 | W3C DOM 标准、可随机访问节点 |
| BeautifulSoup | 第三方 | 高 | 中等 | 非常高 | 复杂解析、网页抓取 | 极其灵活、支持多种解析器、CSS 选择器 |
| lxml.etree | 第三方 | 中等 | 非常快 | 高 | 高性能需求、XPath 查询 | 性能最优、XPath 支持、兼容 ElementTree API |
| xml.sax | 标准库 | 低 | 快 | 低 | 超大文件、流式处理 | 事件驱动、内存效率高、代码较复杂 |
选择建议:
- 日常使用首选
xml.etree.ElementTree:它是标准库,无需额外安装,API 简洁,能满足大部分 XML 读取需求 。 - 需要复杂查询时选
lxml:如果需要使用 XPath 或处理性能要求极高的大文件,lxml是最佳选择。 - 处理 HTML/XML 混合内容时选
BeautifulSoup:当 XML 格式不太规范或需要非常灵活的查找方式时,BeautifulSoup是强大工具 。 - 处理超大文件时考虑
xml.sax:当 XML 文件太大无法全部装入内存时,使用 SAX 解析器。 - 需要完整 DOM 树时用
minidom:当需要完整的文档对象模型进行复杂操作时使用,但注意其内存开销较大 。