XML解析 - ElementTree与SAX和DOM的对比

90 投票
4 回答
35752 浏览
提问于 2025-04-11 09:30

Python有几种方法可以解析XML文件...

我对SAX解析的基本原理有一些了解。它是一种流式解析器,使用事件驱动的接口。

我也了解DOM解析器。它会把XML文件读入内存,并把它转换成可以用Python访问的对象。

一般来说,根据你的需求、内存限制和性能等因素,选择这两种解析器中的一种是比较简单的。

(希望到这里我说的都是对的。)

自从Python 2.5以来,我们还引入了ElementTree。那么它和DOM、SAX相比怎么样呢?它更像哪一个?为什么它比之前的解析器更好呢?

4 个回答

10

ElementTree 的接口更符合 Python 的风格。而且它现在已经包含在标准库里,所以使用它可以减少对其他库的依赖。

其实我更喜欢 lxml,因为它的接口和 ElementTree 类似,但还提供了一些很不错的额外功能,而且性能也很好。

17

最简化的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
84

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。

撰写回答