如何使用python解析超大XML文档

Tags: python xml

在工作时最有吸引力的地方在于可以尽量避免使用昔日的技术。主机、租用线路、COBOL语言......没有人应该要处理这些东西了,对不对?不幸的是,你最终会与现实发生冲突,即使是2014年,大家都知道JSON是最好的方式,你的票务供应商(你无法控制的)会告诉你,只有使用XML导出才能让大容量的数据输出他们的系统。

唉~~~~,好,很好,无所谓。这只是一次性的事情,我不需要照顾和养活这个XML,我只需要解析它并将数据保存到Postgres中,我们就可以利用它。不应该太困难,我需要写一点python脚本…

import xml.etree.cElementTree as ET

tree = ET.parse('huge.xml')
for ticket_node in tree.findall('ticket'):
    #etc...

......这将工作的非常好,如果我们谈论的是一个几MB的XML文档,但是如果遇到的是huge.xml它是1.3GB的巨大文档,这种方法只会融化你的笔记本电脑(以16GB的MacBookPro,一旦python的过程花了超过约3GB的内存,系统变得几乎完全反应迟钝,并且它几乎还没有完成)。回到原点。
首先让我们快速浏览一下我们的数据。

<?xml version="1.0" encoding="UTF-8"?>
<tickets report_date="20140217">
  <ticket>
    <!-- various ticket fields, some of which I want -->
    <comments type="array">
      <comment>
        <!-- various comment fields, some of which I want -->
      </comment>
      <!-- possibly more comment tags -->
    </comments>
  </ticket>
  <!-- many, many ticket tags -->
</tickets>

不是很复杂,作为一个整体它不是一个真正的文件中,<ticket>节点只是一个列表,每一类又是一个小文件,我想挑出几部分出来。我不需要做针对树的任何复杂的遍历,只是希望从每个<ticket>节点获得一些数据然后把它扔掉再读下一个。原来ElementTree的对眼前这个场景提供了一个工具:iterparse()。让我们再试一次:

import xml.etree.cElementTree as ET

for event, element in ET.iterparse('huge.xml'):
    if event == 'end' and element.tag == 'ticket':
        #process ticket...

…什么? !我的笔记本电脑又融化了!跟parse-the-whole-file的方法一样使用了完全相同的内存(和系统响应能力)。到底发生了什么事?
好吧,稍微google了一下,google告诉我,当iterparse()读取元素时,它仍然是在内存中建立了一个完整的文档树,就像我一开始使用parse()方法一样。几个博客和stackoverflow的答案推荐添加element.clear()方法在循环结束时清理你不需要的对象,可以限制内存消耗。我拯救了你的麻烦:它不工作。其他博客,so的答案,甚至一个IBM白皮书表明需要在循环结束时进行更彻底的清扫工作结束:

import lxml.etree as ET #the IBM piece used lxml but I tried cElementTree also

for event, element in ET.iterparse('huge.xml'):
    if event == 'end' and element.tag == 'ticket':
        #process ticket...

    element.clear()

    while elem.getprevious() is not None:
        del elem.getparent()[0]

......哎呀!我溶化了另一台笔记本电脑!

为什么不工作?坦率地说,我不知道。
我稍微离题一下来说说为什么我爱Python。作为一个DBA和系统工程师,我面对着大量的一次性编程挑战。移动这个从这里到那里、Munge数据、将数据从这里迁移到哪里。这种类型的挑战是非常适合于蛮力编程解决问题的这种方式。总之,有时是不值得在建立一个优雅的、易于维护的解决方案上花费任何时间。有时候,你只需要解决这个问题,然后忘掉它。 在处理这类问题上Python最棒的,简洁的语法、良好的设计理念、丰富的库都有助于这个工具,很容易快速解决您碰到的任何问题。即使速度比同等的Java解决方案的10倍还慢,如果需要5分钟的时间写而不是5小时,我更愿意使用python,因为人类工时比CPU工时更有价值。
所有这一切都证明下述方式解决了我的问题,而不会融化的笔记本电脑:

import xml.etree.cElementTree as ET

def process_buffer(buf):
    tnode = ET.fromstring(buf)
    #pull it apart and stick it in the database

inputbuffer = ''
with open('huge.xml','rb') as inputfile:
    append = False
    for line in inputfile:
        if '<ticket>' in line:
            inputbuffer = line
            append = True
        elif '</ticket>' in line:
            inputbuffer += line
            append = False
            process_buffer(inputbuffer)
            inputbuffer = None
            del inputbuffer #probably redundant...
        elif append:
            inputbuffer += line

不是最优雅,或有效率,或者通用的解决方案,但它可以工作。刚刚看了手边的手册,利用其结构的简单性,在解析之前根据xml文件的内容将它切成可管理的块,然后解析和处理每个块,终于可以确保不再需要更长的时间来把它全部处理完。

本文链接:http://www.4byte.cn/learning/120052/ru-he-shi-yong-python-jie-xi-chao-da-xml-wen-dang.html



相关文章