在Python中快速解析大型XML文档的最佳方法是什么?
我现在正在运行一段代码,这段代码是根据《Python Cookbook》第12.5章写的:
from xml.parsers import expat
class Element(object):
def __init__(self, name, attributes):
self.name = name
self.attributes = attributes
self.cdata = ''
self.children = []
def addChild(self, element):
self.children.append(element)
def getAttribute(self,key):
return self.attributes.get(key)
def getData(self):
return self.cdata
def getElements(self, name=''):
if name:
return [c for c in self.children if c.name == name]
else:
return list(self.children)
class Xml2Obj(object):
def __init__(self):
self.root = None
self.nodeStack = []
def StartElement(self, name, attributes):
element = Element(name.encode(), attributes)
if self.nodeStack:
parent = self.nodeStack[-1]
parent.addChild(element)
else:
self.root = element
self.nodeStack.append(element)
def EndElement(self, name):
self.nodeStack.pop()
def CharacterData(self,data):
if data.strip():
data = data.encode()
element = self.nodeStack[-1]
element.cdata += data
def Parse(self, filename):
Parser = expat.ParserCreate()
Parser.StartElementHandler = self.StartElement
Parser.EndElementHandler = self.EndElement
Parser.CharacterDataHandler = self.CharacterData
ParserStatus = Parser.Parse(open(filename).read(),1)
return self.root
我正在处理大约1GB大小的XML文档。有没有人知道更快的方法来解析这些文档?
8 个回答
我推荐你使用 lxml,这是一个针对libxml2库的Python绑定,速度非常快。
根据我的经验,libxml2和expat的性能非常相似。不过我更喜欢libxml2(以及Python的lxml),因为它似乎更新和测试得更积极。而且libxml2的功能更多。
lxml在大多数情况下和 xml.etree.ElementTree 是兼容的。而且它的网站上有很好的文档。
你有没有试过 cElementTree
这个模块?
cElementTree
是从 Python 2.5 版本开始就自带的,路径是 xml.etree.cElementTree。你可以参考一下这个 基准测试。
需要注意的是,从 Python 3.3 开始,cElementTree
就成了默认的实现,所以如果你用的是 Python 3.3 及以上版本,就不需要再做这个更改了。
已移除无效的 ImageShack 链接
看起来你的程序不需要处理DOM(文档对象模型)的功能。我建议你使用(c)ElementTree库。如果你使用cElementTree模块的iterparse函数,就可以逐步处理XML文件,并在事件发生时进行相应的操作。
不过,Fredrik提到的关于使用cElementTree的iterparse函数的建议是:
在解析大文件时,你可以在处理完元素后立即将其删除:
for event, elem in iterparse(source):
if elem.tag == "record":
... process record elements ...
elem.clear()
不过,上面的做法有一个缺点;它不会清除根元素,所以你最终会得到一个包含很多空子元素的单一元素。如果你的文件非常大,而不仅仅是大,这可能会成为一个问题。为了解决这个问题,你需要获取根元素。最简单的方法是启用开始事件,并将第一个元素的引用保存在一个变量中:
# get an iterable
context = iterparse(source, events=("start", "end"))
# turn it into an iterator
context = iter(context)
# get the root element
event, root = context.next()
for event, elem in context:
if event == "end" and elem.tag == "record":
... process record elements ...
root.clear()
lxml.iterparse()不允许这样做。
之前的方法在Python 3.7上不适用,可以考虑以下方式来获取第一个元素。
import xml.etree.ElementTree as ET
# Get an iterable.
context = ET.iterparse(source, events=("start", "end"))
for index, (event, elem) in enumerate(context):
# Get the root element.
if index == 0:
root = elem
if event == "end" and elem.tag == "record":
# ... process record elements ...
root.clear()