杰瑞科技汇

Python minidom 解析如何高效处理XML?

minidom 是 Python 标准库中 xml.dom 模块的一部分,它提供了一个简单、轻量级的文档对象模型接口来解析 XML 文件,它非常易于上手,适合处理结构相对简单的 XML 文档。

Python minidom 解析如何高效处理XML?-图1
(图片来源网络,侵删)

什么是 minidom

minidom 是 "Mini DOM" 的缩写,即“小型文档对象模型”,它将整个 XML 文档加载到内存中,并将其转换为一个树状结构,你可以通过遍历这个树来访问、查询和修改 XML 数据的各个部分。

核心概念:

  • 文档: 整个 XML 文档的根节点,代表整个文档。
  • 节点: 树中的任何一个元素,包括元素节点(如 <book>)、文本节点(如 "Python Guide")、属性节点(如 id="1")等。
  • 元素: 代表 XML 标签,如 <title><author>
  • 属性: 元素的特性,如 idlang
  • 子节点: 一个元素节点内部的直接节点,如 <book> 的子节点是 <title><author>
  • 后代节点: 一个元素节点内部的所有节点,包括子节点的子节点。

准备工作:一个示例 XML 文件

为了演示,我们创建一个名为 books.xml 的文件。

books.xml

Python minidom 解析如何高效处理XML?-图2
(图片来源网络,侵删)
<?xml version="1.0" ?>
<catalog>
    <book id="1">
        <author>Gambardella, Matthew</author>
        <title>XML Developer's Guide</title>
        <genre>Computer</genre>
        <price>44.95</price>
        <publish_date>2000-10-01</publish_date>
    </book>
    <book id="2">
        <author>Ralls, Kim</author>
        <title>Midnight Rain</title>
        <genre>Fantasy</genre>
        <price>5.95</price>
        <publish_date>2000-12-16</publish_date>
    </book>
    <book id="3">
        <author>Corets, Eva</author>
        <title>Maeve Ascendant</title>
        <genre>Fantasy</genre>
        <price>5.95</price>
        <publish_date>2000-11-17</publish_date>
    </book>
</catalog>

解析 XML 的基本步骤

使用 minidom 解析 XML 通常遵循以下三个步骤:

  1. 导入模块from xml.dom import minidom
  2. 解析文件minidom.parse()minidom.parseString()
  3. 操作 DOM 树:遍历节点,获取数据

核心操作详解

1 解析 XML

从文件解析

from xml.dom import minidom
# 打开并解析 XML 文件
# 使用 with 语句可以确保文件被正确关闭
with open('books.xml', 'r', encoding='utf-8') as f:
    dom = minidom.parse(f)
# dom 变量就代表了整个 XML 文档的 DOM 树

从字符串解析 如果你已经有了一个 XML 格式的字符串,可以直接使用 parseString

from xml.dom import minidom
xml_string = """
<catalog>
    <book id="1">
        <title>XML Developer's Guide</title>
    </book>
</catalog>
"""
dom = minidom.parseString(xml_string)

2 获取根元素

解析完成后,dom.documentElement 属性指向整个文档的根元素。

Python minidom 解析如何高效处理XML?-图3
(图片来源网络,侵删)
root = dom.documentElement
print(f"根元素标签名: {root.tagName}")  # 输出: 根元素标签名: catalog

3 遍历子节点

childNodes 属性返回一个包含所有子节点的列表,这个列表包含文本节点(如换行符和空格)和元素节点。

# 获取根元素的所有子节点
children = root.childNodes
print("\n--- 遍历根元素的所有子节点 ---")
for child in children:
    # nodeType == 1 表示这是一个元素节点
    if child.nodeType == child.ELEMENT_NODE:
        print(f"找到元素节点: {child.tagName}")

输出:

--- 遍历根元素的所有子节点 ---
找到元素节点: book
找到元素节点: book
找到元素节点: book

注意minidom 会保留 XML 文件中的格式(如换行和缩进),这些通常以文本节点的形式存在,在遍历时,通常需要检查 nodeType 是否为 ELEMENT_NODE,以过滤掉这些非元素节点。

4 获取特定元素

使用 getElementsByTagName() 方法可以根据标签名获取所有匹配的元素列表。

# 获取所有 <book> 元素
book_elements = root.getElementsByTagName('book')
print(f"\n找到了 {book_elements.length} 个 <book> 元素.")
# 遍历所有 book 元素
for book in book_elements:
    print(f"\n--- 处理一本书 ---")
    # 获取元素的属性
    book_id = book.getAttribute('id')
    print(f"书籍ID: {book_id}")
    # 获取子元素
    # 再次使用 getElementsByTagNameelement = book.getElementsByTagName('title')[0]
    author_element = book.getElementsByTagName('author')[0]
    # 获取元素的文本内容
    # .firstChild.data 是获取文本节点内容的常用方法= title_element.firstChild.data
    author = author_element.firstChild.data
    print(f"书名: {title}")
    print(f"作者: {author}")

输出:

找到了 3 个 <book> 元素.
--- 处理一本书 ---
书籍ID: 1
书名: XML Developer's Guide
作者: Gambardella, Matthew
--- 处理一本书 ---
书籍ID: 2
书名: Midnight Rain
作者: Ralls, Kim
--- 处理一本书 ---
书籍ID: 3
书名: Maeve Ascendant
作者: Corets, Eva

5 获取文本内容

获取元素内部的文本内容是 minidom 中最常见也最容易出错的一步,因为元素内的文本被表示为一个文本节点

正确的方法是:

  1. 获取元素节点。
  2. 访问它的 firstChild 属性,这通常指向其内部的文本节点。
  3. 访问文本节点的 data 属性,得到最终的字符串。
# 错误示范:直接访问 element.data 会失败element = book.getElementsByTagName('title')[0]
# print(title_element.data) # AttributeError: no attribute 'data'
# 正确示范element = book.getElementsByTagName('title')[0]text = title_element.firstChild.data
print(f"正确获取书名: {title_text}")

6 创建和修改 XML

minidom 不仅可以读取,还可以创建和修改 XML。

创建新元素和节点:

# 创建一个新的 <book> 元素
new_book = dom.createElement('book')
new_book.setAttribute('id', '4')
# 创建子元素
new_author = dom.createElement('author')= dom.createElement('title')
# 创建文本节点
author_text = dom.createTextNode('New Author')text = dom.createTextNode('New Book Title')
# 将文本节点添加到元素中
new_author.appendChild(author_text)appendChild(title_text)
# 将子元素添加到 <book> 元素中
new_book.appendChild(new_author)
new_book.appendChild(new_title)
# 将新的 <book> 元素添加到根元素中
root.appendChild(new_book)

将修改后的 DOM 树写回文件:

# toprettyxml() 方法可以将 DOM 树格式化并输出为字符串
# indent="\t" 设置缩进为制表符
new_xml_string = dom.toprettyxml(indent="\t")
# 写入新文件
with open('new_books.xml', 'w', encoding='utf-8') as f:
    f.write(new_xml_string)
print("\n新的 XML 文件已生成: new_books.xml")

完整示例代码

下面是一个完整的、结合了读取、查询和写入的脚本。

from xml.dom import minidom
import os
def parse_and_modify_xml():
    # 1. 解析原始 XML 文件
    try:
        with open('books.xml', 'r', encoding='utf-8') as f:
            dom = minidom.parse(f)
    except FileNotFoundError:
        print("错误: books.xml 文件未找到,请确保该文件在脚本同目录下。")
        return
    root = dom.documentElement
    # 2. 查询并打印所有书籍信息
    print("--- 原始书籍信息 ---")
    books = root.getElementsByTagName('book')
    for book in books:
        book_id = book.getAttribute('id')
        title = book.getElementsByTagName('title')[0].firstChild.data
        author = book.getElementsByTagName('author')[0].firstChild.data
        print(f"ID: {book_id}, 书名: {title}, 作者: {author}")
    # 3. 创建并添加一本新书
    print("\n--- 正在添加新书 ---")
    new_book = dom.createElement('book')
    new_book.setAttribute('id', '4')
    new_author = dom.createElement('author')
    new_author.appendChild(dom.createTextNode('Pragmatic Programmer'))
    new_title = dom.createElement('title')
    new_title.appendChild(dom.createTextNode('The Pragmatic Programmer'))
    new_book.appendChild(new_author)
    new_book.appendChild(new_title)
    root.appendChild(new_book)
    print("新书已添加。")
    # 4. 将修改后的内容保存到新文件
    new_xml_content = dom.toprettyxml(indent="  ")
    # toprettyxml 会添加一个空的文档声明行,我们可以手动处理一下
    # 或者直接写入,通常影响不大
    with open('modified_books.xml', 'w', encoding='utf-8') as f:
        f.write(new_xml_content)
    print("\n修改后的 XML 已保存到 modified_books.xml")
if __name__ == '__main__':
    parse_and_modify_xml()

minidom 的优缺点

优点:

  • Python 标准库:无需安装任何第三方库,开箱即用。
  • API 简单直观:对于简单的 XML 操作,API 设计得比较容易理解。
  • 支持完整的 DOM:可以随意在树中移动、添加、删除节点,非常灵活。

缺点:

  • 内存消耗大:它会将整个 XML 文档加载到内存中,对于非常大的 XML 文件(几百MB或GB级别),这会导致内存不足。
  • 性能较低:相比于更底层的解析器(如 lxml),minidom 的解析和操作速度较慢。
  • 处理空白符childNodes 会包含大量的文本节点(换行、空格),使得遍历变得繁琐,需要额外判断 nodeType

更现代的替代方案:ElementTree

对于大多数应用场景,Python 标准库中的 xml.etree.ElementTree 是比 minidom 更好的选择,它更轻量、更快,并且内存效率更高(特别是配合 iterparse 处理大文件时)。

ElementTree 的一个简单示例:

import xml.etree.ElementTree as ET
tree = ET.parse('books.xml')
root = tree.getroot()
# 遍历所有 book 元素,语法更简洁
for book in root.findall('book'):
    book_id = book.get('id')= book.find('title').text
    author = book.find('author').text
    print(f"ID: {book_id}, 书名: {title}, 作者: {author}")

可以看到,ElementTree 的 API 更加简洁,直接通过 .text 获取文本内容,通过 .find().findall() 查找元素,避免了 minidom 中复杂的节点类型判断。

特性 minidom ElementTree
位置 Python 标准库 Python 标准库
模型 完整 DOM 简化的树模型
内存 高,加载整个文档 低,更高效
性能 较慢 较快
易用性 API 繁琐,需处理节点类型 API 简洁直观
适用场景 简单、小型 XML 文档,需要复杂 DOM 操作 推荐,绝大多数 XML 处理场景

建议

  • 如果只是简单地读取或写入 XML,优先使用 xml.etree.ElementTree
  • 如果你的 XML 文件非常小,或者你需要利用 DOM 模型的强大功能(如在节点间任意移动、复制等),minidom 仍然是一个可行的选择。
分享:
扫描分享到社交APP
上一篇
下一篇