用lxml来解析大型xml文件+命令行中的python
Saturday, 7 August 2010
上周被布置了一个任务,要解析一个大约有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)
这样,一个可以让人方便使用的模块就写好了。