Python3 XML 解析
Parsing, namespaces, XPath-like search, and safe handling of untrusted XML.
Key PointsElementTree covers 95% of XML work; never parse untrusted XML with the default parser — XXE and billion-laughs are real.
Parse and Walk
Python
import xml.etree.ElementTree as ET
xml = """<catalog>
<book id="1"><title>Python</title><price>39.9</price></book>
<book id="2"><title>Go</title><price>45.0</price></book>
</catalog>"""
root = ET.fromstring(xml)
for book in root.findall("book"):
print(book.get("id"), book.findtext("title"), book.findtext("price"))Files and Writing
Python
tree = ET.parse("catalog.xml")
root = tree.getroot()
book = ET.SubElement(root, "book", {"id": "3"})
ET.SubElement(book, "title").text = "Rust"
ET.SubElement(book, "price").text = "49.0"
ET.indent(tree, space=" ") # 3.9+ 美化缩进
tree.write("out.xml", encoding="utf-8", xml_declaration=True)Namespaces
Namespaced documents use {uri}tag in ElementTree. Register a prefix once so the output stays readable.
Python
NS = {"s": "http://www.w3.org/2005/Atom"}
root.findall("s:entry", NS)
root.findtext("s:title", namespaces=NS)
ET.register_namespace("", "http://www.w3.org/2005/Atom")Safe Parsing
Bash
pip install defusedxmlPython
from defusedxml import ElementTree as SafeET
root = SafeET.fromstring(untrusted_bytes) # 禁止 DTD / 实体展开The default expat parser expands entities, which enables XXE file disclosure and exponential blowups. Always use defusedxml for anything a user uploaded.
解析与遍历
Python
import xml.etree.ElementTree as ET
xml = """<catalog>
<book id="1"><title>Python</title><price>39.9</price></book>
<book id="2"><title>Go</title><price>45.0</price></book>
</catalog>"""
root = ET.fromstring(xml)
for book in root.findall("book"):
print(book.get("id"), book.findtext("title"), book.findtext("price"))文件与写出
Python
tree = ET.parse("catalog.xml")
root = tree.getroot()
book = ET.SubElement(root, "book", {"id": "3"})
ET.SubElement(book, "title").text = "Rust"
ET.SubElement(book, "price").text = "49.0"
ET.indent(tree, space=" ") # 3.9+ 美化缩进
tree.write("out.xml", encoding="utf-8", xml_declaration=True)命名空间
带命名空间的文档在 ElementTree 里写成 {uri}tag。注册一次前缀,输出才可读。
Python
NS = {"s": "http://www.w3.org/2005/Atom"}
root.findall("s:entry", NS)
root.findtext("s:title", namespaces=NS)
ET.register_namespace("", "http://www.w3.org/2005/Atom")安全解析
Bash
pip install defusedxmlPython
from defusedxml import ElementTree as SafeET
root = SafeET.fromstring(untrusted_bytes) # 禁止 DTD / 实体展开默认的 expat 解析器会展开实体,从而可能造成 XXE 文件泄露与指数级膨胀。用户上传的 XML 一律用 defusedxml。