处理xml文件

python处理xml文件通常存在多种方式,这里分别以处理简单文件和大文件为例

处理一般文件

处理一般文件,通常使用ElementTree模块,python3.3之后会自动寻找可用的C库来加快速度

1
2
3
4
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET

查询xml

解析根节点**

1
2
3
tree = ET.parse('111.xml')
root = tree.getroot()
#<Element data at 0x1f74dabef48>, so root is data

而获取root的原因在于方便后面解析使用, 通常情况,xml结构标识为<tag attrib1=1>text</tag>tail

1
2
3
4
5
6
7
#source data:<country name="Liechtenstein">
>>> root[0].attrib #获取属性
{'name': 'Liechtenstein'}
>>> root[0][0].text #获取文本
2
>>> root[0][0].tag #获取标签
rank

当然,root也是可以迭代的

1
2
for i in root: 
print(i.tag, i.attrib, i.text, i.tail, sep=';', end='')

也可以是根据某一个标签进行迭代

1
2
3
4
5
6
7
8
for i in root.findall('country'):	#遍历所有符合条件子节点
...
for i in root.find('country'): #遍历第一个符合条件子节点
...
for i in root.findtext('country'): #只遍历文本
...
for i in root.iter('country'): #以当前节点为树节点
...

修改xml

需要注意的是,xml中所有字符均为字符串类型,需要注意字符转换

1
2
3
4
5
rank.text = str(new_rank)  # 必须将int转为str
rank.set("updated", "yes") # 添加属性
del rank.attrib['updated'] #删除属性
tree.write('111.xml') #将数据写入磁盘
root.remove(country) #删除子节点

修改之后的内容只是放在内存中,所以需要将内存里面的数据保存到磁盘中

值得注意的是,python为查询的接口提供了findfindall接口,分别表示查询第一个值就返回和返回所有查询到的值,这就不要注意find并不支持xpath路径查找,如果想要使用xpath查找要使用findall

1
2
for i in root.findall('country[2]/year'):  
print(i.text)

除此之外,还能对元素使用索引和切片,比如:

1
2
3
i = root.findall('country')
print(i[1:2])
#output:[<Element 'country' at 0x000002A4D5794138>]

处理大型xml文件

当然,处理大型文档,除了使用固有的函数模块之外,还可以使用普通文档解析方式,这样只不过会导致取值更麻烦而已

其实只要一想到处理大型数据,就应该第一时间想到迭代器或者生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from xml.etree.ElementTree import iterparse

def parse_and_remove(filename, path):
path_parts = path.split('/')
doc = iterparse(filename, ('start', 'end'))
# Skip the root element
next(doc)

tag_stack = []
elem_stack = []
for event, elem in doc:
if event == 'start':
tag_stack.append(elem.tag)
elem_stack.append(elem)
elif event == 'end':
if tag_stack == path_parts:
yield elem
elem_stack[-2].remove(elem)
try:
tag_stack.pop()
elem_stack.pop()
except IndexError:
pass

iterparse() 方法允许对XML文档进行增量操作。 使用时,你需要提供文件名和一个包含下面一种或多种类型的事件列表: start, end, start-nsend-ns 。由 iterparse() 创建的迭代器会产生形如 (event, elem) 的元组, 其中 event 是上述事件列表中的某一个,而 elem 是相应的XML元素。

start 事件在某个元素第一次被创建并且还没有被插入其他数据(如子元素)时被创建。 而 end 事件在某个元素已经完成时被创建。

yield 之后的下面这个语句才是使得程序占用极少内存的ElementTree的核心特性:

1
elem_stack[-2].remove(elem)

这个语句使得之前由 yield 产生的元素从它的父节点中删除掉。 假设已经没有其它的地方引用这个元素了,那么这个元素就被销毁并回收内存。

对节点的迭代式解析和删除的最终效果就是一个在文档上高效的增量式清扫过程。 文档树结构从始自终没被完整的创建过。尽管如此,还是能通过上述简单的方式来处理这个XML数据。

将字典类型数据转换为xml

存在两种解决方案:

  • 手动构造,以字符串的format函数替代的方式来构造,不过这样显得有点蠢
  • 使用xml.etree.ElementTree模块中的Element函数
1
2
3
4
5
6
7
8
9
10
11
12
from xml.etree.ElementTree import Element, tostringdef
dict_to_xml(tag, d):
elem = Element(tag)
for key, val in d.items():
child = Element(key)
child.text = str(val)
elem.append(child)
return elem
s = {'name': 'GOOG', 'shares': 100, 'price': 490.1}
e = dict_to_xml('stock', s) #<Element 'stock' at 0x000001CE0548C908>
print(tostring(e).decode('utf-8'))
#<stock><name>GOOG</name><shares>100</shares><price>490.1</price></stock>

这样做的目的在于,可以通过查询数据库中的值放进字典中,利用字典生成xml文件

-------------本文结束感谢您的阅读-------------