API名称:
from xml.etree import ElementTree as ET
概念定义:
<country name="Liechtenstein"> <rank>1</rank> <year>2008</year> <gdppc>141100</gdppc> <neighbor name="Austria" direction="E"/> <neighbor name="Switzerland" direction="W"/> </country> 我们把<country>xxx</contry>这种结构称为一个element,country称作element的tag,<></>之间的内容称作element的text或data,<>中的name称作element的attrib,而整个XML树被称作ElementTree。 element是一个名为xml.etree.ElementTree.Element的类,其描述为: class xml.etree.ElementTree.Element(tag, attrib={}, **extra) 此类的所有属性和方法查看: https://docs.python.org/2/library/xml.etree.elementtree.html#element-objects
方法释义:
--读取XML文件 import xml.etree.ElementTree as ET tree = ET.parse('country_data.xml') root = tree.getroot() --读取XML字符串 root = ET.fromstring(country_data_as_string) --获取element object的四大属性tag、text、attrib以及tail root.tag #root element的tag root.text #root element的text root.attrib #root element本身的attrib,dict格式的 root.tail #root element的tag结束到下一个tag之间的text --通过DICT逻辑获取树形结构的text,表示第一个child的第二个child element的text root[0][1].text
element object的方法:
Element.iter(tag) --遍历当前element树所有子节点的element(无论是子节点还是子节点的子节点),找到符合指定tag名的所有element,如果tag为空则遍历当前element树,返回所有节点element(包含当前父节点)。2.7和3.2之前的版本无此方法,可以用getiterator()代替。 Element.findall(tag) --遍历当前节点的直接子节点,找到符合指定tag名的element,返回由element组成的list Element.find(tag) --遍历当前节点的直接子节点,找到符合指定tag名的第一个element Element.get(key) --在当前element中获取符合指定attrib名的value ...其他方法参考官网
修改XML内容:
ElementTree.write(file, encoding="us-ascii", xml_declaration=None, default_namespace=None, method="xml") --将之前的修改写入XML Element.set(key,value) --设置element attrib Element.append(subelement) --新增一个子element,extends(subelements)是3.2的新增用法,输入参数必须是一个element序列 Element.remove(subelement) --删除指定tag的element 示例: >>> for rank in root.iter('rank'): ... new_rank = int(rank.text) + 1 ... rank.text = str(new_rank) ... rank.set('updated', 'yes') ... >>> tree.write('output.xml')
处理含有Namespaces的XML文件:
--有一个如下的XML字符串: <?xml version="1.0"?> <actors xmlns:fictional="http://characters.example.com" xmlns="http://people.example.com"> <actor> <name>John Cleese</name> <fictional:character>Lancelot</fictional:character> <fictional:character>Archie Leach</fictional:character> </actor> <actor> <name>Eric Idle</name> <fictional:character>Sir Robin</fictional:character> <fictional:character>Gunther</fictional:character> <fictional:character>Commander Clement</fictional:character> </actor> </actors>
方法一:在匹配时直接手动加上{uri}前缀 root = fromstring(xml_text) for actor in root.findall('{http://people.example.com}actor'): name = actor.find('{http://people.example.com}name') print name.text for char in actor.findall('{http://characters.example.com}character'): print ' |-->', char.text 方法二:创建自己的namespace别名(其实只是在ns uri很长时可以少写点,实质并没有效率提升) ns = {'real_person': 'http://people.example.com','role': 'http://characters.example.com'} for actor in root.findall('real_person:actor', ns): name = actor.find('real_person:name', ns) print name.text for char in actor.findall('role:character', ns): print ' |-->', char.text --两种方式的输出结果都是: John Cleese |--> Lancelot |--> Archie Leach Eric Idle |--> Sir Robin |--> Gunther |--> Commander Clement
一个比较proxool.xml文件的示例代码:
# -*- coding:utf-8 -*- # 用于进行配置文件的差异比较,2.7和3.2之前element没有iter()的遍历方法可以用getiterator()代替 import sys from xml.etree import ElementTree as ET from xml.dom import minidom # 定义新旧XML文件分别为输入参数1和2 old_file = sys.argv[1] new_file = sys.argv[2] # 定义将新增tag加入旧XML文件的方法 def modify_xml(old_file,new_file): if not new_file: sys.exit(0) tree_old = ET.parse(old_file) # 解析出整个ElementTree tree_new = ET.parse(new_file) global root # 定义全局变量root,只解析一次方便prettify_xml方法调用 root = tree_old.getroot() root_old = tree_old.getroot().find("proxool") # 定位旧XML父节点proxool root_new = tree_new.getroot().find("proxool") old_dict = {} # 定义旧XML文件的tag/text字典 new_dict = {} for e in root_old.getiterator(): # 遍历proxool树的所有节点element,包含其作为父节点的自身 # text为空时不能使用replace方法,因此加上判断;if e.text不能排除空字符' ',只能过滤none和''因此加上strip()过滤 if e.text and e.tag != 'proxool' and e.text.strip() != '': old_dict[e.tag] = e.text.replace("\n", "").replace("\t", "") for e in root_new.getiterator(): if e.text and e.tag != 'proxool' and e.text.strip() != '': new_dict[e.tag] = e.text.replace("\n", "").replace("\t", "") # 至此新旧XML文件的tag/text已经作为字典的元素存在了old_dict和new_dict中,只要比较这两个字典就可以拿到新增tag for tag,text in new_dict.items(): if not old_dict.get(tag): # 当旧XML中找不到对应的tag时,进行tag新增操作 new_tag = ET.Element(tag) # 构造一个element new_tag.text = text # 设置此element的text root_old.append(new_tag) #将此element加入root_old节点下作为其子节点 else: pass # 只为美观,可以不写else tree_old.write(old_file + "_fixed",encoding="UTF-8") # 最后将append的整个ElementTree写入旧XML_fixed文件中,这样注释会丢失 # 新写入的XML项不是那么美观,再美化一下(发现结果更难看了,有待优化) def prettify_xml(filename): strTree = ET.tostring(root) #使用全局变量root new_strTree = minidom.parseString(strTree).toprettyxml() with open(filename,'w') as output: output.write(new_strTree) # 执行函数 modify_xml(old_file,new_file) prettify_xml(old_file + "_fixed")
# Ps:后来发现使用ElementTree解析的XML文件很难美化,且不能处理注释,所以转用minidom处理XML文件了,详见《Python XML解析之DOM》