杰瑞科技汇

Python minidom模块如何解析XML?

minidom 是 Python 标准库中的一个模块,它的全称是 "Minimal DOM"(最小的文档对象模型),它提供了一个轻量级的、用于解析和操作 XML(可扩展标记语言)文档的接口。

Python minidom模块如何解析XML?-图1
(图片来源网络,侵删)

核心概念:DOM

在深入了解 minidom 之前,你需要理解 DOM 的概念:

  • DOM (Document Object Model):当解析一个 XML 文档时,DOM 解析器会将整个 XML 文档读入内存,并将其转换成一个树形结构,这个树的每个节点都代表 XML 文档的一部分(如元素、属性、文本等)。
  • 优点:因为整个文档都在内存中,所以你可以随意地遍历、修改、添加或删除树中的任何一个节点,非常灵活。
  • 缺点:对于非常大的 XML 文件,一次性将其全部读入内存会消耗大量资源,可能导致程序变慢甚至崩溃。

minidom 就是这样一个 DOM 风格的解析器,它比功能更强大的 xml.dom.minidom(它自己)简单,但比 xml.sax(流式解析器)更容易上手。


如何使用 minidom

minidom 的使用通常遵循以下三个步骤:

  1. 解析 XML:从一个字符串或文件中创建 DOM 树。
  2. 操作 DOM 树:遍历、查询、修改节点。
  3. 输出 XML:将修改后的 DOM 树转换回 XML 格式的字符串。

示例 XML 文件

我们以一个简单的 books.xml 文件为例来演示:

books.xml

<?xml version="1.0" ?>
<bookstore>
    <book category="cooking">
        <title lang="en">Everyday Italian</title>
        <author>Giada De Laurentiis</author>
        <year>2005</year>
        <price>30.00</price>
    </book>
    <book category="children">
        <title lang="en">Harry Potter</title>
        <author>J.K. Rowling</author>
        <year>2005</year>
        <price>29.99</price>
    </book>
</bookstore>

解析 XML

minidom 提供了两个主要的解析函数:

parse(string): 从字符串解析

如果你已经将 XML 内容读入了一个 Python 字符串,可以使用这个方法。

from xml.dom import minidom
xml_string = """
<bookstore>
    <book category="cooking">
        <title lang="en">Everyday Italian</title>
        <author>Giada De Laurentiis</author>
        <year>2005</year>
        <price>30.00</price>
    </book>
</bookstore>
"""
# 从字符串解析
dom = minidom.parseString(xml_string)
print("解析成功!")

parse(file): 从文件解析

如果你有一个 XML 文件,可以使用这个方法。

from xml.dom import minidom
# 从文件解析
# 确保 books.xml 文件和你的 Python 脚本在同一个目录下
try:
    dom = minidom.parse('books.xml')
    print("从文件解析成功!")
except FileNotFoundError:
    print("错误:找不到 books.xml 文件")

操作 DOM 树

解析成功后,dom 变量就代表了整个 XML 文档的根节点。

1 获取文档元素(根节点)

documentElement 属性指向 XML 文档的根元素。

# 获取根节点
root_node = dom.documentElement
print(f"根节点标签名: {root_node.tagName}")  # 输出: bookstore

2 遍历子节点

一个节点可以有子节点(如元素、文本、注释等)。childNodes 属性返回一个包含所有子节点的列表。

# 遍历根节点的直接子节点
# 注意:XML 文件中的空白(如换行和空格)也会被解析为文本节点
for child in root_node.childNodes:
    # 我们通常只关心元素节点 (nodeType == 1)
    if child.nodeType == child.ELEMENT_NODE:
        print(f"发现一个子元素: {child.tagName}")

3 查找特定节点

这是最常用的操作之一。

  • getElementsByTagName(tag_name): 获取所有指定标签名的节点列表。

    # 获取所有 <book> 节点
    all_books = dom.getElementsByTagName('book')
    print(f"总共找到 {all_books.length} 本书。")
    # 遍历所有 <book> 节点
    for book in all_books:
        print(f"--- 处理一本书 ---")
        # 获取元素的属性
        category = book.getAttribute('category')
        print(f"类别: {category}")
        # 获取特定的子元素节点
        # 注意:get 返回一个节点列表
        title_nodes = book.getElementsByTagName('title')
        if title_nodes: # 确保列表不为空
            title_node = title_nodes[0]
            # 获取元素的文本内容
            # data 属性直接获取文本
            # firstChild.data 也可以
            title_text = title_node.firstChild.data
            print(f"标题: {title_text}")
        # 同样获取作者
        author_nodes = book.getElementsByTagName('author')
        if author_nodes:
            author_text = author_nodes[0].firstChild.data
            print(f"作者: {author_text}")

4 修改节点

你可以修改节点的属性和内容。

# 假设我们已经获取了第一个 book 节点
first_book = all_books[0]
# 1. 修改属性
first_book.setAttribute('category', 'updated_cooking')
print(f"已将第一本书的类别修改为: {first_book.getAttribute('category')}")
# 2. 修改文本内容
# 获取 price 节点
price_node = first_book.getElementsByTagName('price')[0]
# 获取其文本节点
text_node = price_node.firstChild
# 修改文本节点的数据
text_node.data = '35.00'
print(f"已将第一本书的价格修改为: {price_node.firstChild.data}")

5 创建和添加新节点

你也可以创建新的元素和文本,并将它们添加到树中。

# 创建一个新的 <book> 元素
new_book = dom.createElement('book')
new_book.setAttribute('category', 'web')
# 创建 <title> 元素= dom.createElement('title')setAttribute('lang', 'en')
# 创建文本节点text = dom.createTextNode('Learning Python')
# 将文本节点添加到 <title> 元素中appendChild(title_text)
 元素添加到 <book> 元素中
new_book.appendChild(new_title)
# 将新的 <book> 元素添加到根节点 <bookstore>
# 为了演示,我们把它加到最后
root_node.appendChild(new_book)
print("已成功添加一本新书。")

输出 XML

当你完成所有修改后,通常需要将 DOM 树转换回 XML 字符串。

toprettyxml(): 格式化输出

这个方法会将 DOM 树转换成格式化、易于阅读的 XML 字符串,它非常漂亮,但可能包含很多多余的空白。

# 将整个 DOM 树转换为格式化的 XML 字符串
pretty_xml_string = dom.toprettyxml(indent="  ")
print("\n--- 格式化后的 XML ---")
print(pretty_xml_string)

输出示例:

<?xml version="1.0" ?>
<bookstore>
  <book category="updated_cooking">lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>35.00</price>
  </book>
  <book category="children">lang="en">Harry Potter</title>
    <author>J.K. Rowling</author>
    <year>2005</year>
    <price>29.99</price>
  </book>
  <book category="web">lang="en">Learning Python</title>
  </book>
</bookstore>

toxml(): 紧凑输出

如果你不需要格式化的输出,只是想获取一个干净的 XML 字符串,可以使用 toxml()

# 获取紧凑格式的 XML 字符串
compact_xml_string = dom.toxml()
print("\n--- 紧凑格式的 XML ---")
print(compact_xml_string)

输出示例:

<?xml version="1.0" ?><bookstore><book category="updated_cooking"><title lang="en">Everyday Italian</title><author>Giada De Laurentiis</author><year>2005</year><price>35.00</price></book><book category="children"><title lang="en">Harry Potter</title><author>J.K. Rowling</author><year>2005</year><price>29.99</price></book><book category="web"><title lang="en">Learning Python</title></book></bookstore>

完整代码示例

下面是一个将上述所有步骤整合在一起的完整脚本。

from xml.dom import minidom
# --- 1. 解析 ---
try:
    dom = minidom.parse('books.xml')
    print("从文件 'books.xml' 解析成功!")
except FileNotFoundError:
    print("错误:找不到 'books.xml' 文件,请确保文件在脚本同目录下。")
    exit()
# --- 2. 操作 ---
# 获取根节点
bookstore = dom.documentElement
# 查找并修改第一本书
books = bookstore.getElementsByTagName('book')
if books.length > 0:
    first_book = books[0]
    print(f"\n--- 修改第一本书 ---")
    print(f"原始类别: {first_book.getAttribute('category')}")
    first_book.setAttribute('category', 'updated_cooking')
    print(f"新类别: {first_book.getAttribute('category')}")
    # 修改价格
    price_node = first_book.getElementsByTagName('price')[0]
    print(f"原始价格: {price_node.firstChild.data}")
    price_node.firstChild.data = '35.00'
    print(f"新价格: {price_node.firstChild.data}")
# 添加一本新书
print("\n--- 添加一本新书 ---")
new_book = dom.createElement('book')
new_book.setAttribute('category', 'fantasy')
= dom.createElement('title')setAttribute('lang', 'en')appendChild(dom.createTextNode('The Lord of the Rings'))
new_author = dom.createElement('author')
new_author.appendChild(dom.createTextNode('J.R.R. Tolkien'))
new_year = dom.createElement('year')
new_year.appendChild(dom.createTextNode('1954'))
new_book.appendChild(new_title)
new_book.appendChild(new_author)
new_book.appendChild(new_year)
bookstore.appendChild(new_book)
print("已成功添加一本新书。")
# --- 3. 输出 ---
# 将修改后的 DOM 树写入一个新文件
with open('updated_books.xml', 'w', encoding='utf-8') as f:
    # toprettyxml 可能会产生很多空行,我们进行一些清理
    pretty_xml_str = dom.toprettyxml(indent="  ")
    # 移除空行
    lines = [line for line in pretty_xml_str.split('\n') if line.strip()]
    f.write('\n'.join(lines))
print("\n已将更新后的 XML 保存到 'updated_books.xml' 文件。")
# 打印到控制台看看效果
print("\n--- 最终的 XML 内容 (格式化) ---")
print(dom.toprettyxml(indent="  "))

minidom 的优缺点

优点

  1. 简单易用:API 直观,对于小型 XML 文件的处理非常方便。
  2. 功能完整:支持标准的 DOM 操作,如增、删、改、查、遍历。
  3. 内置标准库:无需安装任何第三方库,开箱即用。

缺点

  1. 内存消耗大:和所有 DOM 解析器一样,它会将整个 XML 文档加载到内存中,对于几百兆甚至更大的文件,这会是一个严重的问题,可能导致 MemoryError
  2. 性能相对较低:因为需要构建整个树结构,所以解析速度通常比流式解析器(如 SAX)慢。
  3. 空白节点问题:它会将 XML 文件中的所有空白(包括缩进和换行)解析为文本节点,这给遍历和查找元素节点带来了一些麻烦,需要通过 nodeType 来过滤。

何时使用 minidom

  • 使用 minidom

    • 当你处理的 XML 文件很小时。
    • 当你需要频繁地、随机地访问和修改 XML 文档的不同部分时。
    • 当你只需要一个简单的解决方案,而不想引入复杂的第三方库时。
  • 考虑其他方案

    • 对于非常大的 XML 文件:使用 xml.sax(流式解析器)或 lxml.etree 的迭代解析功能。
    • 对于需要高性能和强大功能的复杂 XML 处理:强烈推荐使用第三方库 lxml,它比 minidom 快得多,功能也更强大(支持 XPath、XSLT 等),API 设计也很优秀。lxml 是目前 Python 中处理 XML的事实标准。
分享:
扫描分享到社交APP
上一篇
下一篇