通过
import os
import sys
import fcntl
import xml.etree.ElementTree as ET
global example_xml
example_xml = '/home/example/example.xml
# 读取xml
lockf = None
if os.path.exists(example_xml):
lockf = open(example_xml)
fcntl.flock(lockf, fcntl.LOCK_EX)
tree = ET.parse(example_xml)
root = tree.getroot()
logging.info("%s open and root" % example_xml)
else:
logging.error("%s not found!" % example_xml)
sys.exit(-1)
# 解析 name 和 memory
try:
example_name = root.find("name").text
logging.info("example_name is %s" % example_name)
example['name'] = example_name
...
except:
logging.exception("Can't get example_name!")
sys.exit(-2)
# 修改添加子对象(3级) <app><color><red id='0'></red></color></app>
target = tree.find('app/color/red')
if target is None:
print "No app/color/red tag in example.xml"
sys.exit(-1)
target.attrib['id'] = '0'
# 完整添加子节点
e_size = "<size><high redid=\"0\" mode=\"square\" sizeset=\"%d\" /></size>" % size_set
new = ET.fromstring(e_size)
target = root.find(new.tag)
if target is not None:
print "Tag <%s> exists in example.xml" % new.tag
sys.exit(-3)
root.append(new)
以下是用 ElementTree 在 Python 中解析 XML摘要,便于后续自己查询:使用ET处理XML确实非常方便直观,推荐使用。不过,我在实践编程时在项目上使用这些方法并没有完全实现我的目标,也许我使用方法还有问题。实际开发过程中,我使用了同事给我的参考代码进行整合,见上文。
xml.etree.ElementTree (以下简称 ET)。它提供了轻量级的 Python 式的 API ,它由一个 C 实现来提供。
ElementTree 生来就是为了处理 XML ,它在 Python 标准库中有两种实现。一种是纯 Python 实现例如 xml.etree.ElementTree ,另外一种是速度快一点的 xml.etree.cElementTree 。
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
从 Python 3.3 开始,ElementTree 模块会自动寻找可用的 C 库来加快速度
将 XML 解析为树的形式
XML 是一种分级的数据形式,所以最自然的表示方法是将它表示为一棵树。ET 有两个对象来实现这个目的 - ElementTree 将整个 XML 解析为一棵树, Element 将单个结点解析为树。
XML 文件例子
<?xml version="1.0"?>
<doc>
<branch name="testing" hash="1cdf045c">
text,source
</branch>
<branch name="release01" hash="f200013e">
<sub-branch name="subrelease01">
xml,sgml
</sub-branch>
</branch>
<branch name="invalid">
</branch>
</doc>
加载并且解析这个 XML :
import xml.etree.cElementTree as ET
tree = ET.ElementTree(file='doc1.xml')
抓根结点元素:
root 是一个 Element 元素
root = tree.getroot()
root.tag, root.attrib
根元素没有任何状态,可以找到自己的子结点:
for child_of_root in root:
print child_of_root.tag, child_of_root.attrib
找到元素
Element 对象有一个 iter
方法可以对子结点进行深度优先遍历。 ElementTree 对象也有 iter
方法来提供便利。
for elem in tree.iter():
print elem.tag, elem.attrib
iter
方法接受一个标签名字,然后只遍历那些有指定标签的元素:
for elem in tree.iter(tag='branch'):
print elem.tag, elem.attrib
Element 有一些关于寻找的方法可以接受 XPath 作为参数。 find 返回第一个匹配的子元素, findall 以列表的形式返回所有匹配的子元素, iterfind 为所有匹配项提供迭代器。这些方法在 ElementTree 里面也有。
for elem in tree.iterfind('branch/sub-branch'):
print elem.tag, elem.attrib
在 branch 下面找到所有标签为 sub-branch 的元素。然后给出如何找到所有的 branch 元素,用一个指定 name 的状态即可:
for elem in tree.iterfind('branch[@name="release01"]'):
print elem.tag, elem.attrib
建立 XML 文档
ET 提供了建立 XML 文档和写入文件的便捷方式。 ElementTree 对象提供了 write 方法。
root = tree.getroot()
del root[2]
root[0].set('foo', 'bar')
for subelem in root:
print subelem.tag, subelem.attrib
import sys
tree.write(sys.stdout)
建立一个全新的元素,ET 模块提供了 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)
使用 iterparse 来处理 XML 流
XML 文档通常比较大, ET 提供一个特殊的工具,用来处理大型文档,并且解决了内存问题,这个工具叫 iterparse 。
案例XML文件如下:
<?xml version="1.0" standalone="yes"?>
<site>
<regions>
<africa>
<item id="item0">
<location>United States</location> <!-- Counting locations -->
<quantity>1</quantity>
<name>duteous nine eighteen </name>
<payment>Creditcard</payment>
<description>
<parlist>
[...]
用一个简单的脚本来计数有多少 location 元素并且文本内容为“Zimbabwe”。这是用 ET.parse 的一个标准的写法:
tree = ET.parse(sys.argv[2])
count = 0
for elem in tree.iter(tag='location'):
if elem.text == 'Zimbabwe':
count += 1
print count
所有 XML 树中的元素都会被检验。当处理一个大约 100MB 的 XML 文件时,占用的内存大约是 560MB ,耗时 2.9 秒。
但是,并不需要在内存中加载整颗树。它检测我们需要的带特定值的 location 元素。其他元素被丢弃。这是 iterparse 的来源:
count = 0
for event, elem in ET.iterparse(sys.argv[2]):
if event == 'end':
if elem.tag == 'location' and elem.text == 'Zimbabwe':
count += 1
elem.clear() # discard the element
print count
这个循环遍历 iterparse 事件,检测“闭合的”(end)事件并且寻找 location 标签和指定的值。在这里 elem.clear() 是关键 - iterparse 仍然建立一棵树,只不过不需要全部加载进内存,这样做可以有效的利用内存空间。
处理同样的文件,这个脚本占用内存只需要仅仅的 7MB ,耗时 2.5 秒。速度的提升归功于生成树的时候只遍历一次。相比较来说, parse 方法首先建立了整个树,然后再次遍历来寻找我们需要的元素(所以慢了一点)。
参考