小编典典

用Python解析大型XML文档的最快方法是什么?

python

我目前正在根据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

我正在使用大小约为1 GB的XML文档。有谁知道解析这些的更快方法?


阅读 148

收藏
2020-12-20

共1个答案

小编典典

我看起来好像您不需要程序中的任何DOM功能。我将第二次使用(c)ElementTree库。如果使用cElementTree模块的iterparse函数,则可以遍历xml并在事件发生时对其进行处理。

但是请注意,Fredriks关于使用cElementTree iterparse函数的建议:

要解析大型文件,您可以在处理完元素后立即删除它们:

for event, elem in iterparse(source):
    if elem.tag == "record":
        ... process record elements ...
        elem.clear()

上述模式有一个缺点。它不会清除根元素,因此最终将得到一个带有大量空子元素的单个元素。如果您的文件很大,而不只是很大,这可能是一个问题。要变通解决此问题,您需要动手使用root元素。最简单的方法是启用启动事件,并将对第一个元素的引用保存在变量中:

# 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"))

is_first = True

for event, elem in context:
    # get the root element
    if is_first:
        root = elem
        is_first = False
    if event == "end" and elem.tag == "record":
        ... process record elements ...
        root.clear()
2020-12-20