用lxml来解析大型xml文件+命令行中的python

上周被布置了一个任务,要解析一个大约有600MB左右的xml文件,从中提取所需的信息然后输出成一个csv文件。从来没有做过类似东西的我,加上不太熟悉的python、linux和vim,这样一个简单的东西花了一天半才解决,不过解决的还算比较完美吧,用lxml这个库,原本以为要至少10几分钟的解析过程其实只用了1分钟左右,说起来,还是c比较强大啊,lxml的底层使用c实现的,换成python恐怕就够呛了。好了废话不多说,讲讲lxml的用法和我在做这个任务里碰到的几个问题吧,权当复习和备份。

用lxml载入xml文件

lxml是c中的libxml的python实现,在保证效率的情况下,为程序员免去了内存管理方面的麻烦,具体介绍大家还是移步它的官网吧。

首先在python里import lxml的etree模块,然后用etree里的parse函数从文件中解析xml,解析得到的是一个ElementTree的实例,用这个实例的getroot函数就能得到xml中的root。root是对象Element的一个实例,对这个root可以做indexing,即用过root[n]可以得到root下相应的子节点,这些子节点同样也是Element的实例,所以通过root[n][m]就可遍历各个节点。

>>>from lxml import etree

>>>tree = etree.parse(open(“file_name”,“rb”))

>>>root = tree.getroot()

另外,对一个Element还可以进行iterate操作,iterate会依次遍历Element下的所有子节点、子节点的子节点,然后按照顺序,返回一个所有节点的序列。

如果我们有这样一个xml:

<root>

<child>

<grandson1/>

<grandson2>name1</grandson2>

</child>

<child>

<grandson1/>

<grandson2>name2</grandson2>

</child>

</root>

那么:

>>>root[0]    #返回第一个child节点的Element实例

>>>root[0][0]    #返回第一个child中grandson1

>>>root.iter()    #按顺序返回root中所有节点

那么如何得到各个节点中的信息呢?其实也很方便,用element.text、element.tag可以得到节点的内容和节点的名字。另外用element.get(“attribute_name”)还可以得到节点中attribute的值。

如:

>>>for child in root:

>>>    for son in child.iter():

>>>        print son.tag, “:”, son.text

这样一段代码就可以遍历每个上面那个xml中每一个child中的grandchild的名字了。

另外,lxml还提供了丰富的写xml的功能,和读写html的功能,可以说是一场强大,有兴趣的童鞋可以自行研究~

像运行*nix命令一样用python模块

我做的这个解析功能是给同事用的,所以自己加了点代码好让同事在命令行中利用这个代码。

我想实现的功能是,当同事在bash中运行:

$:python parser.py –f some_xml_file.xml

便可以直接跑我的代码来解析xml了。当同事运行:

$:python parser.py

的时候,会把这个模块的用法打印出来。具体代码如下,各个行的作用我用注释标注了:

if __name__ == “__main__”:

#引用OptionParse模块:
from optparse import OptionParser

#初始化一个Parser,这时可以用usage参数写下模块说明:
parser = OptionParser(usage=”%prog [options] xml_filename\n”
“available option:   -f    indicates the xml file name”)

#添加一个option,缩写为-f,全称是–file:
parser.add_option(‘-f’, ‘–file’)

#从命令行中得到option和arguments:
options, args = parser.parse_args()

#如果file不为空,则运行模块:
if options.file:
sys.exit(parse_feed(options.file))

#若file是空,则打印出模块用法:
else:
parser.print_usage()
sys.exit(1)

这样,一个可以让人方便使用的模块就写好了。