xml.dom.minidom
和xml.sax
大槪是Python中解析XML文件最广为人知的两个模块了,
原因一是这两个模块自Python 2.0以来就成为Python的标准库;
二是网上关于这两个模块的 使用方面的资料最多。作为主要解析XML方法的两种实现,
DOM需要将整个XML文件加载到内存中并解析为一棵树,
虽然使用较为简单,但占用内存较多,性能方面不占优势,
并且不够Pythonic;而SAX是基于事件驱动的,虽不需要全部装人XML文件,
但其处理过程却较为复杂实际上Python中对XML的处理还有更好的选择,
ElementTree
便是其中一个, 一般情况下使用ElementTree
便已足够。
ElementTree
ElementTree
从Python2.5开始成为标准模块,cEkraentTree
是 ElememTree
的Cython实现,
速度更快,消耗内存更少,性能上更占优势,在实际使用过程中应该尽量优先使用cElemcntTree
。
由于两者使用方式上完全兼容本文将两者看做一个物件, 除非说明不再刻意区分。
ElememTree
在解析XML文件上具有以下特性:
- 使用简单。它将整个XML文件以树的形式展示,每一个元素的属性以字典的形式表示,
- 非常方便处理。
- 内存上消耗明显低于DOM解折。由于
ElementTree
底层进行了一定的优化, - 并且它的
iterparse
解析工具支持SAX事件驱动,能够以迭代的形式返回XML部分数据结构, - 从而避免将整个XML文件加载到内存中,因此性能上更优化,相比于SAX使用起来更为简单明了。
- 支持XPath查询,非常方便获取任意节点的值。
这里需要说明的是,一般情况指的是:XML文件大小适中,对性能要求并非非常严格。
如果在实际过程中需要处理的XML文件大小在GB或近似GB级别,
第三方模块 lxml
会获得较优的处理结果。
dementtree
解析 XML 文件
下面结合具体的实例来说明 dementtree
解析 XML 文件常用的方法。
需要解析的XML 实例如下:
with open('xx_test2.xml', 'w' ) as fo:
fo.write('''<?xml version="1.0" encoding="UTF-8" ?>
<books>
<book id="1001">
<name>面纱</name>
<userid>root2</userid>
<info>请记住我,虽然再见必须说</info>
</book>
<book id="1002">
<name>人生第一次</name>
<userid>root3</userid>
<info>愿他们、我们的一生平淡而有意义</info>
</book>
</books>''')
!cat xx_test2.xml
<?xml version="1.0" encoding="UTF-8" ?> <books> <book id="1001"> <name>面纱</name> <userid>root2</userid> <info>请记住我,虽然再见必须说</info> </book> <book id="1002"> <name>人生第一次</name> <userid>root3</userid> <info>愿他们、我们的一生平淡而有意义</info> </book> </books>
模块ElementTrec
主要存在两种类型EkmeutTree
和Ekment
,它们支持的方法以及对应的使用示例如下表。
表 1: ElementTree
主要的方法和使用示例
主要的方法、属性 | 方法说明以及示例 |
---|---|
getroot() |
返回 xml 文档的根节点 >>> import xml.etree.ElementTree as ET >>> tree = ET.ElementTree(file ="test.xml") >>> root = tree.getroot() >>> print root <Element 'systems' at 0x26cbffD> >>> print root.tag systems |
find(match) findall(match) findtext(match, default=None) |
同 Element 相关的方法类似,只是从跟节点开始搜索 >>> for i in root.findall("system/purpose"): ... print i.text ... automation test manual test >>> print root.findtext("system/purpose") automation test >>> print root.find("system/purpose") <Element 'purpose' at 0x26d2170> |
iter(tag=None) |
从 xml 根结点开始,根据传入的元素的 tag 返回所有的元素集合的迭代器 >>> for i in tree.iter(tag = "command"): ... print i.text ... echo root:mytestpwdsudo /usr/sbin/chpasswd mkdir /TEST; chmod 777 /TEST echo root:mytestpwdsudo /usr/sbin/chpasswd mkdir /TEST; chmod 777 /TEST |
iterfind(match) |
根据传入的 tag 名称或者 path 以迭代器的形式返回所有的子元素 >>> for i in tree.iterfind("system/purpose"): ... print i.text ... automation test manual test |
表 2: Element
主要的方法和使用示例
主要的方法、属性 | 方法说明以及示例 |
---|---|
tag |
字符串,用来表示元素所代表的名称 >>> print root[1].tag # 输出 system |
text |
表示元素所对应的具体值 >>> print root[1].text # 输出空串 |
attrib |
用字典表示的元素的属性 >>> print root[1].attrib # 输出 {'platform': 'aix', 'name': 'aixtext'} |
get(key, default=None) |
根据元素属性字典的 key 值获取对应的值,如果找不到对应的属性,则返回 default >>> print root[1].attrib.get("platform") # 输出 aix |
items() |
将元素属性以 (名称,值) 的形式返回 >>> print root[1].items() #[('platform', 'aix'), ('name', 'aixtext')] |
keys() |
返回元素属性的 key 值集合 >>> print root[1].keys() # 输出 ['platform', 'name'] |
find(match) |
根据传入的 tag 名称或者 path 返回第一个对应的 element 对象,或者返回 None |
findall(match) |
根据传入的 tag 名称或者 path 以列表的形式返回所有符合条件的元素 |
findtext(match, default=None) |
根据传入的 tag 名称或者 path 返回第一个对应的 element 对象对应的的值,即 text 属性,如果找不到则返回 default 的设置 |
list(elem) |
根据传入的元素的名称返回其所有的子节点 >>> for i in list(root.findall("system/system_type")): ... print i.text ... # 输出 virtual virtual |
前面我们提到elementree
的iterparse
工具能够避免将整个XML文件加载到内存。
从而解决当读人文件过大内存而消耗过多的问题。
iterparse
返回一个可以迭代的由元组(时间,元素)组成的流对象,
支持两个参数:source
和events
。
其中 event
有4种选择:startElement
、endElement
、startElementNS
和 endElementNS
(默认为 endElement),
分别与 SAX 解析的 startElement()
、endElement()
、startElementNS()
和 endElementNS()
方法一一对应。
最后,我们来看一个使用 iterparse
的示例:统计 userid
在整个 XML 文档中出现的总次数。
count=0
import xml.etree.ElementTree as ET
for event,elem in ET.iterparse("xx_test2.xml"): # 对 iterparse 的返回值进行迭代
if event== "end":
if elem.tag=="userid":
count+=1
elem.clear()
print(count)
2