解析XML的常用方法有两种:DOM和SAX。DOM就是根据XML文档建一棵树,有了这棵树查询插入删除什么的都可以做了。SAX就是事件驱动,进入一个Element做什么啦退出一个Element做什么啦。在Python中的用法分别参见:

但本文的主角并不是上面链接里的xml.dom.minidom和xml.sax,而是一个并非Python内置的叫做lxml的库。这个库非常可爱,使用方法非常简单:

from lxml import *

先从DOM方法说起吧。对一个XML文档进行parse可以得到一个ElementTree对象,对其调用getroot方法可以得到根节点,是一个Element对象。主要的操作都是围绕Element这个类展开的。对一个DOM树的主要操作也就下面这些:

  • 标签名

    Element对象的tag成员。

  • 子节点

    getchildren()方法。但每个Element都是一个list,包含了所有子节点,可以用for loop遍历,也可以用[]直接操作。

  • 父节点

    getparent()方法。

  • 左邻居

    getprevious()方法。

  • 右邻居

    getnext()方法。

  • 创建节点

    etree.Element(‘name’)

  • 创建子节点

    etree.SubElement(parent,’name’)

  • 附加子节点

    parent.append(node)

  • 插入子节点

    parent.insert(pos,node)

  • 删除子节点

    parent.remove(node)

  • 移动/复制节点

    移动的话只用正常的赋值操作就可以了(注意这时赋值操作不是复制而是移动,即同一个节点不能出现在一棵DOM树的两个地方)。反倒是复制稍微麻烦一些,要调用copy模块的deepcopy方法。

  • 属性

    在创建节点时可以以**args形式给出,如:

    root = etree.Element('root', age='18')
    

    也可以直接对节点进行操作,用get和set方法。

    还可以使用节点的attrib成员,是一个dict,在里面直接改。

  • 文字

    文字有两种,一种是节点内(text成员),一种是节点后(tail),直接设置即可。没有烦人的TextNode了。

    不过有一个问题是,一个包含n个子节点的节点内部可以有n+1块文字,但第一块文字不在任何子节点后怎么办?答案是由该节点而不是子节点设置。

  • 子树递推

    前面说Element是list,可以for i in node。但是这只是在直接子节点中循环。如果要在整个子树中循环,用for i in node.iter()。

    其实iter()还可以给参数,这里就不说了。

  • XPath查询

    xpath()方法。有一个默认命名空间的问题需要注意。如果设置了默认命名空间,那么不加命名空间前缀的标签名是匹配不到的。可是又没有前缀,怎么办呢?这时要给xpath()方法传入一个dict,key是自定义的,value是默认命名空间。在xpath的查询表达式中用key作为前缀就可以了。

  • 串行化

    处理完了总归要输出的,只需调用etree.tostring(root)。一些可选的参数有xml_declaration/encoding/method/pretty_print等。

lxml很好的一点是对event-driven parsing也有很好的支持。这又分两种。第一种是建树的时候报告事件,可以根据事件进行相应的操作,甚至释放子树以节省空间。第二种是用回调函数实现SAX方法。

第一种方法主要用到iterparse()。第二种方法要自己定义ParseTarget,并用自己定义的ParseTarget建立自定义parser,再用这个自定义parser进行parse。

命名空间方面,一般只需要对根节点设置默认命名空间就可以了,也就是root.nsmap设置为{None: ‘http://xxx.com/xxx’}即可。

Links: