Python解析巨型XML

久没有更新博客了吧,主要是最近比较忙,到了新的环境需要学习的东西还挺多的,像我这么好学的人当然就一心扑在学习进步的道路上了。不过既然我都登上来了,那就写写最近碰到的一个case吧。

事情是需要处理一个客户提供的数据源,格式是xml。要说处理xml,在Python的世界里首先还是会先想到lxml吧。不过这次的情况有点特殊,这个xml的大小比较bt,gzip压缩后大小1.9G,未压缩状态11G…… 所以,通常用lxml建立element tree的方法是不行了。这点本人已亲身尝试,本以为服务器16G的内存应该够用,结果从开始时内存就迅速飙升,最后悲剧,本人向System Administrator掩面表示很傻很天真。

好吧,碰到这样的情况,大概也就只能想到像SAX那样的事件解析了,翻了一下lxml的文档,果然有类似的解析方法── etree.iterparse() ,默认是在所有元素闭合的时候会触发,不过可以指定event和tag来减少噪音。

OK,立马操刀上阵:

#! /usr/bin/env python
# test.py

import sys
from lxml import etree

counter = 0
for action, elem in etree.iterparse(sys.stdin, tag='Item'):
    counter += 1
print counter

这段脚本从stdin读取数据,然后当每次发现一个Item元素闭合的时候,累加计数器,最后输出整个文件所有的Item个数。脚本是很简单的,接下来只需要将输入流重定向到这个脚本即可。但不知是不是我这儿的gzcat有问题,反正用 gzcat test.xml.gz | less 这样的命令机器的内存还是会崩掉的,所以最终用了gunzip输出到stdout的方式:

gunzip -c test.xml.gz | test.py

可是很快SA又来找我了,因为内存再次悲剧…… 痛定思痛,突然醒悟,我这和直接建etree有啥区别? 解析出来的元素不还是存在内存里么…… 要处理这么大的xml,看来还是需要手动做一下gc才行,如此便有了如下版本:

#! /usr/bin/env python
# test.py

import sys
from lxml import etree

counter = 0
for action, elem in etree.iterparse(sys.stdin, tag='Item'):
    counter += 1
    elem.clear()
    if elem.getprevious() is not None:
        del elem.getparent()[0]
print counter

这儿有两次gc处理,一次是清空Item元素内部的数据,但此时由于无法删除Item本身,所以交由后面的那段 if 段来处理,在下一个元素事件触发是进行清理。由于我这个xml是一个比较规整的结构,Item的平级元素没有其他类型,所以我这儿只需删除前一个前一个平级元素即可,如果需要解析的xml像我这样指定了tag事件,但是相应tag又有其他类型的平级元素,那么可以将 if 改成 while 来清除那些被忽略的元素。

好了,解释到此结束,再次运行该脚本,脚本欢快了,内存欢快了,我也欢快了,嗯。

最后来个每篇一吐槽,用啥xml嘛,那么多文本数据格式,可读性也只有比xml好嘛。想起上回弄SOAP客户端,看各种库帮助半天,烦得要死,最后还是直接建HTTP连接手动发数据收数据了,把简单的东西复杂化,这也是xml功不可没的一点了吧。

对“Python解析巨型XML”的5条评论

  1. Avatar

    标准的没事找抽型!!

    纯文本才是正道

    当然了,楼主最后醒悟还是很提倡的

    展开回复(1)

    收起回复

  2. Avatar
    回复:hah: 标准的没事找抽型!! 纯文本才是正道 当然了,楼主最后醒悟还是很...

    确实,11G的东西,我提出来的数据也就100M而已……

  3. Avatar
  4. Avatar
    回复:匿名: 用sed抽取数据可以么?

    sed以行为单位处理,对包含多行值的xml元素没有很好的支持。如果都是单行的话,确实可以用grep+sed来搞定的。

  5. Avatar

    正好需要,赞一个。据说基于树模式的模块解析方法,所需内存是原文件的10倍。

发表评论

评论备注:

  1. 留言时的头像是Gravatar提供的服务。
  2. By submitting a comment here you grant this site a perpetual license to reproduce your words and name/web site in attribution. So, you don't fully own your words, so to speak.