XML解析 - ElementTree与SAX和DOM的对比
Python有几种方法可以解析XML文件...
我对SAX解析的基本原理有一些了解。它是一种流式解析器,使用事件驱动的接口。
我也了解DOM解析器。它会把XML文件读入内存,并把它转换成可以用Python访问的对象。
一般来说,根据你的需求、内存限制和性能等因素,选择这两种解析器中的一种是比较简单的。
(希望到这里我说的都是对的。)
自从Python 2.5以来,我们还引入了ElementTree。那么它和DOM、SAX相比怎么样呢?它更像哪一个?为什么它比之前的解析器更好呢?
4 个回答
ElementTree 的接口更符合 Python 的风格。而且它现在已经包含在标准库里,所以使用它可以减少对其他库的依赖。
其实我更喜欢 lxml,因为它的接口和 ElementTree 类似,但还提供了一些很不错的额外功能,而且性能也很好。
最简化的DOM实现:
链接.
Python提供了一个完整的、符合W3C标准的XML DOM实现(xml.dom),还有一个简化版,叫做xml.dom.minidom。这个简化版比完整的实现要简单和小巧。不过,从“解析”的角度来看,它和标准DOM有一样的优缺点——也就是说,它会把所有内容都加载到内存中。
考虑一个基本的XML文件:
<?xml version="1.0"?>
<catalog>
<book isdn="xxx-1">
<author>A1</author>
<title>T1</title>
</book>
<book isdn="xxx-2">
<author>A2</author>
<title>T2</title>
</book>
</catalog>
使用minidom的一个可能的Python解析器是:
import os
from xml.dom import minidom
from xml.parsers.expat import ExpatError
#-------- Select the XML file: --------#
#Current file name and directory:
curpath = os.path.dirname( os.path.realpath(__file__) )
filename = os.path.join(curpath, "sample.xml")
#print "Filename: %s" % (filename)
#-------- Parse the XML file: --------#
try:
#Parse the given XML file:
xmldoc = minidom.parse(filepath)
except ExpatError as e:
print "[XML] Error (line %d): %d" % (e.lineno, e.code)
print "[XML] Offset: %d" % (e.offset)
raise e
except IOError as e:
print "[IO] I/O Error %d: %s" % (e.errno, e.strerror)
raise e
else:
catalog = xmldoc.documentElement
books = catalog.getElementsByTagName("book")
for book in books:
print book.getAttribute('isdn')
print book.getElementsByTagName('author')[0].firstChild.data
print book.getElementsByTagName('title')[0].firstChild.data
需要注意的是,xml.parsers.expat是Python与Expat非验证XML解析器的接口(docs.python.org/2/library/pyexpat.html)。
xml.dom包还提供了一个异常类DOMException,但在minidom中并不支持这个类!
ElementTree XML API:
链接.
ElementTree使用起来简单得多,而且比XML DOM占用的内存少。此外,还有一个C语言实现(xml.etree.cElementTree)。
使用ElementTree的一个可能的Python解析器是:
import os
from xml.etree import cElementTree # C implementation of xml.etree.ElementTree
from xml.parsers.expat import ExpatError # XML formatting errors
#-------- Select the XML file: --------#
#Current file name and directory:
curpath = os.path.dirname( os.path.realpath(__file__) )
filename = os.path.join(curpath, "sample.xml")
#print "Filename: %s" % (filename)
#-------- Parse the XML file: --------#
try:
#Parse the given XML file:
tree = cElementTree.parse(filename)
except ExpatError as e:
print "[XML] Error (line %d): %d" % (e.lineno, e.code)
print "[XML] Offset: %d" % (e.offset)
raise e
except IOError as e:
print "[XML] I/O Error %d: %s" % (e.errno, e.strerror)
raise e
else:
catalogue = tree.getroot()
for book in catalogue:
print book.attrib.get("isdn")
print book.find('author').text
print book.find('title').text
ElementTree 使用起来简单多了,因为它把 XML 树(基本上)看作一个列表结构,而属性则用字典来表示。
ElementTree 处理 XML 树所需的内存比 DOM 少得多(所以速度也更快),而通过 iterparse
解析的开销和 SAX 相当。此外,iterparse
会返回部分结构,你可以在处理完这些结构后立即丢弃它们,从而保持内存使用量不变。
ElementTree 在 Python 2.5 中的功能相对完整的 XML 库来说比较少,但对于很多应用来说已经足够了。如果你需要一个验证解析器或者完整的 XPath 支持,lxml 是个不错的选择。虽然它以前不太稳定,但自从 2.1 版本后我就没有遇到过问题。
ElementTree 和 DOM 有些不同,DOM 中的节点可以访问它们的父节点和兄弟节点。处理实际文档而不是数据存储时也有点麻烦,因为文本节点并不被视为真正的节点。在下面的 XML 片段中:
<a>This is <b>a</b> test</a>
字符串 test
将成为元素 b
的所谓 tail
。
总的来说,我推荐在 Python 中进行 XML 处理时默认使用 ElementTree,而对于特定问题则可以考虑使用 DOM 或 SAX。