XML是可拓展标记语言,用来传输和储存数据
解析XML的三种方法
常见的XML编程接口有DOM和SAX,Python有三种方法解析XML:SAX, DOM,ElementTree。
- SAX means simple API for XML:
python标准库包含SAX解析器,用事件驱动模型,通过在解析XML过程中触发一个个事件并调用用户定义的回调函数来处理XML文件。 - DOM
将XML数据在内存中解析成一个树,通过对树的操作来操作XML - ElementTree:
就像一个轻量级的DOM,具有很好的API,代码可用性好,速度快,消耗的内存小
!!!这里我们首选使用xml.etree.ElementTree
模块
使用ElementTree解析XML:
xml.etree.ElementTree
模块提供了一个轻量级API,与DOM相比,ET的速度更快,API使用更直接方便。与SAX相比,ET.iterparse
函数同样提供了按需解析的功能,不会一次性在内存中读入整个文档。ET的性能与SAX模块大致相仿,但是它的API更加高层次,用户使用起来更加便捷。
在标准库中提供了对ET的两种实现,一种是纯python版本,一种是c语言实现的版本,这里使用c语言版本更好,因为它的速度更快,消耗内存更少,所以这样导入模块:
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
Python3.3之后,不需要这样导入,因为ET模块会自动优先使用C加速器
将XML文档解析成树:
!!!这里有两种对象,一个为ElementTree对象,另一个为Element对象
XML文档例子:
<?xml version="1.0"?>
<doc>
<branch name="codingpy.com" hash="1cdf045c">
text,source
</branch>
<branch name="release01" hash="f200013e">
<sub-branch name="subrelease01">
xml,sgml
</sub-branch>
</branch>
<branch name="invalid">
</branch>
</doc>
加载这个文档并且解析:
>>> import xml.etree.ElementTree as ET
>>> tree = ET.ElementTree(file='doc1.xml') # 加载这个文档并且解析,赋值为tree
---------------------------
>>> tree.getroot() # 获取这个树的根元素
<Element 'doc' at 0x11eb780>
---------------------------
>>> root = tree.getroot()
>>> root.tag, root.attrib # 查看这个根元素的属性,根元素为一个Element对象
('doc', {}) # 根元素并没有属性
---------------------------
>>> for child_of_root in root: # 根元素具备遍历其直接子元素的接口
... print child_of_root.tag, child_of_root.attrib # .tag就是标签名
...
branch {'hash': '1cdf045c', 'name': 'codingpy.com'}
branch {'hash': 'f200013e', 'name': 'release01'}
branch {'name': 'invalid'}
---------------------------
>>> root[0].tag, root[0].text # 也可以通过索引值来访问特定的子元素
('branch', '\n text,source\n ')
查找元素:
Element
和ElementTree
对象都有iter
方法,可以对对象之下的所有子元素进行深度优先遍历DFS,所以这里最简单的办法是:
>>> for elem in tree.iter():
... print elem.tag, elem.attrib
...
doc {}
branch {'hash': '1cdf045c', 'name': 'codingpy.com'}
branch {'hash': 'f200013e', 'name': 'release01'}
sub-branch {'name': 'subrelease01'} # 这个是上面一条的子元素,所以这是深度优先遍历
branch {'name': 'invalid'}
这个方法还能接受tag
参数,然后遍历标签为这个tag的元素:
>>> for elem in tree.iter(tag='branch'):
... print elem.tag, elem.attrib
使用XPath查找元素更加方便:
Element对象中有一些find方法可以接受Xpath路径作为参数,find方法会返回第一个匹配的子元素,findall会以列表的形式返回所有匹配的子元素,iterfind则返回一个所有匹配元素的迭代器。
ElementTree也有上述方法,查找就是从根节点开始的。
>>> for elem in tree.iterfind('branch/sub-branch'):
... print elem.tag, elem.attrib
...
sub-branch {'name': 'subrelease01'}
上面是根据路径查找
下面是查找所有具备某个name属性的元素:
>>> for elem in tree.iterfind('branch[@name="release01"]'):
... print elem.tag, elem.attrib
...
branch {'hash': 'f200013e', 'name': 'release01'}
修改或者创建XML文档:
修改文档可以通过调整Element对象来完成:
>>> root = tree.getroot()
>>> del root[2] # 相当于删除了元素
>>> root[0].set('foo', 'bar') # 相当于增加了新的属性
>>> for subelem in root:
... print subelem.tag, subelem.attrib
...
branch {'foo': 'bar', 'hash': '1cdf045c', 'name': 'codingpy.com'}
branch {'hash': 'f200013e', 'name': 'release01'}
创建XML文档使用SubElement工厂函数:
>>> a = ET.Element('elem')
>>> c = ET.SubElement(a, 'child1')
>>> c.text = "some text"
>>> d = ET.SubElement(a, 'child2')
>>> b = ET.Element('elem_b')
>>> root = ET.Element('root')
>>> root.extend((a, b))
>>> tree = ET.ElementTree(root)
>>> tree.write(sys.stdout)
<root><elem><child1>some text</child1><child2 /></elem><elem_b /></root>
使用iterparse解析XML:
因为ET是将文档加载成树保存到内存里面,所以一旦XML很大,也会遇到内存消耗太大的问题,这里需要用到ET里面类似SAX的特殊工具iterparse,它可以循序的解析XML。
这里只是介绍有这么个东西,暂时不介绍如何使用,笔者用到了再更新~