如何迭代解析 Python 中的大型 XML 文件?

How to iteratively parse a large XML file in Python?

我需要处理一个大约 8Gb 的大 .XML 文件。 文件结构(简化)类似于以下内容:

<TopLevelElement>
    <SomeElementList>
        <Element>zzz</Element>
        ....and so on for thousands of rows
    </SomeElementList>
    <Records>
        <RecordType1>
            <RecordItem id="aaaa">
                <SomeData>
                    <SomeMoreData NameType="xxx">
                        <NameComponent1>zzz</NameComponent1>
                        ....
                        <AnotherNameComponent>zzzz</AnotherNameComponent>
                    </SomeMoreData>
                </SomeData>
            </RecordItem>
        ..... hundreds of thousands of items, some are quite large.
        </RecordType1>
        <RecordType2>
            <RecordItem id="cccc">
            ...hundreds of thousands of RecordType2 elements, slightly different from RecordItems in RecordType1 
            </RecordItem>
        </RecordType2>
    </Records>
</TopLevelElement>

我需要提取 RecordType1 和 RecordType2 元素中的一些子元素。有条件决定哪些记录项需要处理哪些字段 需要提取。单个RecordItems不超过120k(有些有大量的文本数据,我不需要)。

这是代码。函数 get_all_records 接收以下输入:a) XML 文件的路径; b) 记录类别('RecordType1'或'RecordType2'); c) 选择什么名称组件

from xml.etree import cElementTree as ET

def get_all_records(xml_file_path, record_category, name_types, name_components):
    context = ET.iterparse(xml_file_path, events=("start", "end"))
    context = iter(context)
    event, root = next(context)
    all_records = []
    for event, elem in context:
        if event == 'end' and elem.tag == record_category and elem.attrib['action'] != 'del':
            record_contents = get_record(elem, name_types=name_types, name_components=name_components, record_id=elem.attrib['id'])
            if record_contents:
                all_records += record_contents
            root.clear()
    return all_records

我已经对记录的数量进行了试验,代码在大约一分钟内很好地处理了 100k RecordItems(只有 Type1,到达 Type2 花费的时间太长了)。 尝试处理更多的记录(我拿了一百万),最终导致 ElementTree.py 中的 MemoryError。 所以我猜尽管 root.clear() 语句没有释放内存。

一个理想的解决方案是 RecordItems 一次读取一个,处理,然后从内存中丢弃,但我不知道该怎么做。 从 XML 的角度来看,两个额外的元素层(TopLevelElement 和 Records)似乎使任务复杂化。 我是 XML 和相应的 Python 库的新手,所以非常感谢详细的解释!

遍历巨大的 XML 文件总是很痛苦。

我将从头到尾检查所有过程,建议保持低内存但最大化解析速度的最佳做法。

首先不需要将ET.iterparse存储为变量。就像

一样遍历​​它

for event, elem in ET.iterparse(xml_file, events=("start", "end")): 这个迭代器是为,好吧...,迭代而创建的,除了当前标记之外,没有在内存中存储任何其他内容。此外,您不需要 root.clear() 使用这种新方法,只要您的硬盘 space 允许它存储巨大的 XML 文件,您就可以使用它。

您的代码应如下所示:

from xml.etree import cElementTree as ET

def get_all_records(xml_file_path, record_category, name_types, name_components):
    all_records = []
    for event, elem in ET.iterparse(xml_file_path, events=("start", "end")):
        if event == 'end' and elem.tag == record_category and elem.attrib['action'] != 'del':
            record_contents = get_record(elem, name_types=name_types, name_components=name_components, record_id=elem.attrib['id'])
            if record_contents:
                all_records += record_contents
    return all_records

另外,请仔细考虑您需要存储all_records的整个列表的原因。如果它仅用于在过程结束时写入 CSV 文件 - 这个原因还不够好,并且在扩展到更大的 XML 文件时可能会导致内存问题。

确保在该行发生时将每个新行写入 CSV,将内存问题转化为 none 问题。

P.S.

如果您需要在找到主标签之前存储多个标签,以便在您查看 XML 文件时解析此历史信息 - 只需将其存储在一些新变量中。每当 XML 文件中的未来数据使您返回到您知道已经出现的特定标签时,这就会派上用场。