繁体   English   中英

Python xml ElementTree 可以解析非常大的 xml 文件吗?

[英]Can Python xml ElementTree parse a very large xml file?

我正在尝试解析结构化标记数据的大文件(> 2GB),但内存不够。这是针对这种情况的 XML 解析类的最佳方式。请提供更多详细信息。

查看iterparse()函数。 可以在此处找到有关如何使用它来解析非常大的文档的说明。

大多数 DOM 库——比如 ElementTree——在核心中构建整个文档模型。 传统上,当您的模型太大而无法一次性放入内存时,您需要使用更面向流的解析器,例如xml.sax

这通常比您预期的更难,尤其是在用于像一次处理整个 DOM 这样的高阶操作时。

你的xml文档有没有可能像

<entries>
  <entry>...</entry>
  <entry>...</entry>
</entries>

这将允许您以更 ElementTree 友好的方式处理数据的子集?

我见过的唯一可以处理这类事情的 API 是 pulldom:

http://docs.python.org/library/xml.dom.pulldom.html

Pulldom 使用 SAX API 构建部分 DOM 节点; 通过将特定的子树作为一个组拉入,然后在完成后丢弃它们,您可以获得 SAX 的内存效率和 DOM 使用的理智。

这是一个不完整的 API; 当我使用它时,我不得不修改它以使其完全可用,但它可以作为基础。 我不再使用它了,所以我不记得我必须添加什么; 只是一个预先警告。

这很慢。

XML 是处理大型数据集的一种非常糟糕的格式。 如果您对源数据有任何控制权,并且如果它对数据集有意义,那么最好将数据分成更小的块,以便您可以完全解析到内存中。

另一种选择是使用 SAX API,但直接使用它们做任何重要的事情都非常痛苦。

是的,十年后,已经有许多处理大文件的新解决方案。 下面给大家推荐一款。

比如文件test.xml的内容如下

<?xml version="1.0" encoding="UTF-8"?>
<breakfast_menu>
    <food>
        <name>Strawberry Belgian Waffles</name>
        <price>$7.95</price>
        <description>
        Light Belgian waffles covered with strawberries and whipped cream
        </description>
        <calories>900</calories>
    </food>
    <food>
        <name>Berry-Berry Belgian Waffles</name>
        <price>$8.95</price>
        <description>
        Belgian waffles covered with assorted fresh berries and whipped cream
        </description>
        <calories>900</calories>
    </food>
    ......
</breakfast_menu>

使用 SimplifiedDoc 的解决方案如下:

from simplified_scrapy import SimplifiedDoc, utils

doc = SimplifiedDoc()
doc.loadFile('test.xml', lineByline=True)

for food in doc.getIterable('food'):
    print (food.children.text)

结果:

['Strawberry Belgian Waffles', '$7.95', 'Light Belgian waffles covered with strawberries and whipped cream', '900']
...

正如其他回答者所说, ElementTree是一个 DOM 解析器,尽管它有iterparse()方法。

为了减少内存占用,我使用了一个真正的 SAX 解析器。 是我用于解决方案的链接。 这是官方文档。 这是我的 XML:

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
    <entity storageTableName="table7113" tableName="TableBusinessName">
        <attribute storageFieldName="field7114" fieldName="BusinessName1" />
        <attribute storageFieldName="field7115" fieldName="BusinessName2" />
        . . .
    </entity>
    . . .
</metadata>

这是代码:

import xml.sax


class ModelNameHandler(xml.sax.ContentHandler):
    ENTITY_TAG = "entity"
    STORAGE_TABLE_NAME_ATTR = "storageTableName"
    TABLE_NAME_ATTR = "tableName"
    ATTRIBUTE_TAG = "attribute"
    STORAGE_FIELD_NAME_ATTR = "storageFieldName"
    FIELD_NAME_ATTR = "fieldName"

    def __init__(self):
        self.entity_code = None
        self.entity_names = {}
        self.attr_names = {}

    def startElement(self, tag, attributes):
        if tag == self.ENTITY_TAG:
            self.entity_code = attributes[self.STORAGE_TABLE_NAME_ATTR]
            entity_name = attributes[self.TABLE_NAME_ATTR]
            self.entity_names[self.entity_code] = entity_name
        elif tag == self.ATTRIBUTE_TAG:
            attr_code = attributes[self.STORAGE_FIELD_NAME_ATTR]
            key = self.entity_code + "." + attr_code
            attr_name = attributes[self.FIELD_NAME_ATTR]
            self.attr_names[key] = attr_name


def get_model_names(file):
    parser = xml.sax.make_parser()
    parser.setFeature(xml.sax.handler.feature_namespaces, 0)
    handler = ModelNameHandler()
    parser.setContentHandler(handler)
    parser.parse(file)

    return handler.entity_names, handler.attr_names

工作足够快。

以防万一,多一点细节:

import my_package as p


if __name__ == "__main__":

    with open('<my_path>/<my_file>.xml', 'r', encoding='utf_8') as file:
        entity_names, attr_names = p.get_model_names(file)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM