在python的ElemenTree库中,如何仅将iterparse用于外层

时间:2016-02-10 06:50:08

标签: python memory xml-parsing elementtree

我有一个包含大量记录的XML文件。结构就像

<root> <record> <somedata>here</somedata> <complexdata> <info>text</info> <location>here</location> </complexdata> . . . </record> <record> <somedata>not here</somedata> <complexdata> <info>more text</info> <location>there</location> </complexdata> . . . </record> . . . </root>

因此该文件包含数百万个<record>结构,但每个记录结构只有很少的数据,只有一个或两个递归级别。

我想用python的ElementTree库解析文件。为了减少内存占用,我使用iterparse遍历所有记录结构。另一方面,如果每个记录本身都可以完全加载到内存中并通过普通的树方法访问,那么它会很方便。

如何告诉elementtree使用外层iterparse,然后切换到将整个记录加载到对象中进行处理?

1 个答案:

答案 0 :(得分:1)

  

为减少内存占用,我使用iterparse来浏览所有记录   结构。另一方面,如果每个记录本身都会很方便   将被完全加载到内存中并通过普通树访问   方法

iterparse()返回Element,而Element则提供常规树方法,例如您可以使用findall()搜索元素,也可以迭代元素,例如for child in elmt:。您的<record>代码会以元素的形式返回 - 您只需在合适的时刻抓住它们:

使用此xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<root>
    <record level="0">
        <somedata>here</somedata>
        <complexdata>
            <info>hello</info>
            <location>here</location>
        </complexdata>
        <record level="1"><record level="2">records</record></record>
    </record>
    <record level="0">
        <somedata>not here</somedata>
        <record level="1"></record>
        <complexdata>
            <record level="1"><record level="2"></record></record>
            <info>goodbye</info>
            <location>there</location>
        </complexdata>
    </record>
</root>

此代码:

import xml.etree.ElementTree as etree

record_iter = etree.iterparse("xml.xml", ["start", "end"]) 
_, root = next(record_iter)

record_nesting_level = -1 

for (event, elmt) in record_iter:
    if elmt.tag == "record":
        if event == "start": 
            record_nesting_level += 1
        else:  #then it's an "end" event
            if record_nesting_level == 0: #then the elmt should be a top level record tag
                print("{} -- level {}".format(elmt.tag, elmt.attrib["level"])) 
                #Call normal etree methods on elmt:
                for child in elmt:
                    print("\t{}".format(child.tag))

                root.clear()  #empty out the root element so that at most only 
                              #one toplevel record tag will be in memory at a time


            record_nesting_level -= 1

产生这个结果:

record -- level 0
    somedata
    complexdata
    record
record -- level 0
    somedata
    record
    complexdata

对评论的回应:

next()返回迭代器中的下一个项目。 for-in循环,例如:

for elmt in record_iter:

重复调用next(record_iter)并将结果分配给elmt变量。您可以随时在迭代器上手动调用next()

在示例代码中,next(record_iter)返回迭代器中的第一个项,即元组:

("start", <Element 'root'>)

以下内容:

 _, root = next(record_iter)

只是一个变体:

x, y = (1, 2)

print(x)  #=> 1
print(y)  #=> 2

我本来可以写的:

x, root = next(record_iter)

但是因为我不关心事件名称,并且我不会使用x变量,所以我选择了一个名为_的变量。这是一个完全合法的变量名称。 (研究过函数式语言的人通常会使用_来表示他们不关心的变量。)

elmt.clear()不删除<record>元素,而是删除其内容。如果让iterparse()将1亿个空元素对象附加到<root>元素,它们将耗尽内存。多少?您将不得不进行一些测试 - 但是为什么在编写root.clear()时很容易呢?