Python读取XML的四种方法及实例

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 标准库 超大文件、流式处理 事件驱动、内存效率高、代码较复杂

选择建议:

  1. 日常使用首选 xml.etree.ElementTree:它是标准库,无需额外安装,API 简洁,能满足大部分 XML 读取需求 。
  2. 需要复杂查询时选 lxml :如果需要使用 XPath 或处理性能要求极高的大文件,lxml 是最佳选择。
  3. 处理 HTML/XML 混合内容时选 BeautifulSoup :当 XML 格式不太规范或需要非常灵活的查找方式时,BeautifulSoup 是强大工具 。
  4. 处理超大文件时考虑 xml.sax:当 XML 文件太大无法全部装入内存时,使用 SAX 解析器。
  5. 需要完整 DOM 树时用 minidom:当需要完整的文档对象模型进行复杂操作时使用,但注意其内存开销较大 。

参考来源