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

核心概念:DOM
在深入了解 minidom 之前,你需要理解 DOM 的概念:
- DOM (Document Object Model):当解析一个 XML 文档时,DOM 解析器会将整个 XML 文档读入内存,并将其转换成一个树形结构,这个树的每个节点都代表 XML 文档的一部分(如元素、属性、文本等)。
- 优点:因为整个文档都在内存中,所以你可以随意地遍历、修改、添加或删除树中的任何一个节点,非常灵活。
- 缺点:对于非常大的 XML 文件,一次性将其全部读入内存会消耗大量资源,可能导致程序变慢甚至崩溃。
minidom 就是这样一个 DOM 风格的解析器,它比功能更强大的 xml.dom.minidom(它自己)简单,但比 xml.sax(流式解析器)更容易上手。
如何使用 minidom
minidom 的使用通常遵循以下三个步骤:
- 解析 XML:从一个字符串或文件中创建 DOM 树。
- 操作 DOM 树:遍历、查询、修改节点。
- 输出 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 的优缺点
优点
- 简单易用:API 直观,对于小型 XML 文件的处理非常方便。
- 功能完整:支持标准的 DOM 操作,如增、删、改、查、遍历。
- 内置标准库:无需安装任何第三方库,开箱即用。
缺点
- 内存消耗大:和所有 DOM 解析器一样,它会将整个 XML 文档加载到内存中,对于几百兆甚至更大的文件,这会是一个严重的问题,可能导致
MemoryError。 - 性能相对较低:因为需要构建整个树结构,所以解析速度通常比流式解析器(如 SAX)慢。
- 空白节点问题:它会将 XML 文件中的所有空白(包括缩进和换行)解析为文本节点,这给遍历和查找元素节点带来了一些麻烦,需要通过
nodeType来过滤。
何时使用 minidom?
-
使用
minidom:- 当你处理的 XML 文件很小时。
- 当你需要频繁地、随机地访问和修改 XML 文档的不同部分时。
- 当你只需要一个简单的解决方案,而不想引入复杂的第三方库时。
-
考虑其他方案:
- 对于非常大的 XML 文件:使用
xml.sax(流式解析器)或lxml.etree的迭代解析功能。 - 对于需要高性能和强大功能的复杂 XML 处理:强烈推荐使用第三方库
lxml,它比minidom快得多,功能也更强大(支持 XPath、XSLT 等),API 设计也很优秀。lxml是目前 Python 中处理 XML的事实标准。
- 对于非常大的 XML 文件:使用
