【问题标题】:Can Python xml ElementTree parse a very large xml file?Python xml ElementTree 可以解析一个非常大的 xml 文件吗?
【发布时间】:2010-09-14 08:17:24
【问题描述】:

我正在尝试解析一个大文件 (> 2GB) 的结构化标记数据,但内存不足。对于这种情况,这是 XML 解析类的最佳方式。请详细说明。

【问题讨论】:

    标签: python xml


    【解决方案1】:

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

    【讨论】:

    • iterparse() 函数将解决您的问题,我最近在 iterparse 的帮助下轻松解析了一个 1GB 的 xml 文档:)
    【解决方案2】:

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

    这通常比您预期的要难,尤其是在用于高阶操作(例如同时处理整个 DOM)时。

    你的xml文档有没有可能很简单

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

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

    【讨论】:

      【解决方案3】:

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

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

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

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

      很慢。

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

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

      【讨论】:

      • 我刚刚发现了 pulldom 库,如果它以正常速度运行的话,它看起来非常棒。它是如此的慢。我用完整的 dom xml ElementTree 解析了一个 xml 文件,并将其放入数据帧需要 60 秒(1 分钟)。 pulldom 库花了 600 秒(10 分钟)只是读取文件(没有解析)。这东西怎么这么慢?
      【解决方案4】:

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

      例如文件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']
      ...
      

      【讨论】:

        【解决方案5】:

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

        为了减少内存占用,我使用了真正的 SAX 解析器。 Here 是我用于解决方案的链接。 Here's官方文档。这是我的 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)
        

        【讨论】:

          猜你喜欢
          • 2015-09-22
          • 1970-01-01
          • 2019-03-23
          • 2023-03-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-06
          • 2017-08-31
          相关资源
          最近更新 更多