这些年,大家都喜欢谈论大数据,那我们也来看看所谓大数据下,常规思路是如何走入困境,又应该如何化解矛盾。在这里我们以DrugBank的数据库为例子,讨论一下如何用Python解析大型的XML。

0b0. 上车前系好安全带

一般大家会在发生内存泄漏,填满所有可用内存之前把程序停掉。又或者在申请内存空间时,返回空间不足,从而导致程序意外终止。在内存不足的情况下,Python可不会管你,如果不自己加点保护措施,会直接把系统搞崩。因此,首先要用Python的resource模块上一个内存使用的保险。例如我们将该Python进程最大可使用内存设为3GB,超过3GB将抛出Memory Error异常。

import resource

# Max Memory Used 3GB
MAX_MEM = 5*1024*1024*1024

_ = resource.prlimit(0,resource.RLIMIT_AS,(MAX_MEM,MAX_MEM))

0b1. 开车啦

传统的parse无法正确加载
系好安全带之后会抛出异常而不是计算机崩溃

然后就翻车了
这就是大数据所面临的经典问题

贫穷的你,内存不够大

当然其实内存大了也没用

0b10. 分而治之

针对drugbank数据库,简单分析可以看出,这个数据库是由一个drugbank标签包围的一堆drug标签所构成的。无法在普通计算机中直接读入到内存也只是因为drug标签稍微有一点多——虽然也不是那么地多,毕竟才一万多个。那么一个非常直观的想法就是只解析每个drug标签,而不必把这一万多个drug标签一起解析。这就相当于我们不去直接加载整个数据库,而一条一条数据加载、处理、释放内存。

仔细观察drugbank的xml文件可以看出:其一,以drug单词开头的标签还有不少;其二,文件格式还是不错的,不同标签都正确换行,且不同层次的标签缩进空格也有。那么根据这两个特征,我们可以通过打开文件,逐行分析drug标签的开闭,截取需要的drug标签片段,然后解析。值得注意的是,drug标签里会在drug-interaction标签里再包含一个drug标签,因此如果文件格式不太妙的话,可能还要注意一下premature end of data in tag drug 的问题,需要额外判断一下标签开闭状态。当然drugbank的数据文件还不错,只需要简单地截取就可以了。下面给出一个函数原型,权当抛砖引玉。

from lxml.etree import parse
import tarfile
from io import BytesIO

def drugs(place='drugbank.tar.xz'):
    raw_zip = tarfile.open(name=place)
    XML = raw_zip.extractfile(raw_zip.getmember('full database.xml'))

    buf = None
    enter = False

    for line in XML:
        if line.startswith(b'<drug '):
            buf = BytesIO()
            enter = True
        if enter:
            buf.write(line)
        if line.startswith(b'</drug>'):
            buf.seek(0)
            enter = False
            yield parse(buf)

留下评论