概述
对比其他 Python 处理 XML 的方案,xml.etree.ElementTree 模块(下文我们以 ET 来表示)相对来说比较简单,接口也较友好。 官方文档 里面对 ET 模块进行了较为详细的描述,总的来说,ET 模块可以归纳为三个部分:ElementTree类,Element类以及一些操作 XML 的函数Func。
XML 可以看成是一种树状结构,ET 使用ElementTree类来表示整个 XML 文档,使用Element类来表示 XML 的一个结点。对整 XML 文档的操作一般是对ElementTree对象进行,而对 XML 结点的操作一般是对Element对象进行。
使用XML时,一般引入一个ET库,xml.etree.ElementTree
(ET
简而言之)
XML的操作可以理解为增删改查的过程,依据概述中的总体思路,判断是对XML文档操作还是对结点进行操作
1、解析XML文件
?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
按照ElementTree操作文件,Element操作对象的思路;ET 模块支持从一个 XML 文件构造ElementTree
对象,使用 ET 模块的parse()
函数来从指定的 XML 文件构造一个ElementTree
对象:
#-*-encoding:utf-8-*-
import xml.etree.ElementTree as ET
# 获取 XML 文档对象 ElementTree
tree = ET.parse('country_data.xml')
# 获取 XML 文档对象的根结点 Element
root = tree.getroot()
# 打印根结点的名称
print("根节点的名称:",root.tag)
#获取其他属性
print('root的属性:',root.attrib)
2、解析XML对象
解析XML对象使用Element。
ET 模块的fromstring()
函数提供从 XML 字符串构造一个Element
对象的功能。
xml_str = ET.tostring(root)
print("根节点tostring:",xml_str)
root = ET.fromstring(xml_str)
print("根节点的tag:",root.tag)
for child in root:
print('root的其他属性:',child.tag,child.attrib)
print(root[0][1].text)
print(root[0][2].text)
输出为:
root的其他属性: country {'name': 'Liechtenstein'}
root的其他属性: country {'name': 'Singapore'}
root的其他属性: country {'name': 'Panama'}
2008
141100
使用 ET 模块的tostring()
函数来将上面我们构造的root
对象转化为字符串,注意这里的“字符串”不同于python中的str,然后使用fromstring()
函数重新构造一个Element
对象,并赋值给root
变量,这时root
代表整个 XML 文档的根结点。
3、构造XML
使用ET 模块的 Element
类以及SubElement()
函数。 可以使用Element
类来生成一个Element
对象作为根结点(XML对象),然后使用ET.SubElement()
函数生成子结点(结点对象)。
parent = ET.Element('parent')
child1 = ET.SubElement(parent,'child1')
child1.text = 'leehan.cpm'
child2 = ET.SubElement(parent,'child2')
child2.attrib['greeting']='hello world'
child3 = ET.SubElement(parent,'child2')
child2.text="www.baidu.com"
xml_str = ET.tostring(parent,encoding='UTF-8')
print(xml_str)
with open("newxml1.xml",'w') as f:
f.write(str(xml_str))
# 如果需要输出到文件中,可以继续使用ElementTree.write()方法来处理:
tree = ET.ElementTree(parent)
tree.write("newxml2.xml",encoding='utf-8')
由于使用write写入的时候规定类型为str,写入后发可以使用nodpad打开,但是使用浏览器打开时显示为空,因此使用ElementTree.write()
方法来处理
<?xml version="1.0"?>
-<parent>
<child1>leehan.cpm</child1>
<child2 greeting="hello world"/>
<child3>www.baidu.com</child3>
</parent>
4、XML结点查找与更新
Element
类提供了Element.iter()
方法来查找指定的结点。Element.iter()
会递归查找所有的子结点,以便查找到所有符合条件的结点。
# 获取 XML 文档对象 ElementTree
tree = ET.parse('data.xml')
# 获取 XML 文档对象的根结点 Element
root = tree.getroot()
# 递归查找所有的 neighbor 子结点
for neighbor in root.iter('neighbor'):
print neighbor.attrib
输出结果:
{'direction': 'E', 'name': 'Austria'}
{'direction': 'W', 'name': 'Switzerland'}
{'direction': 'N', 'name': 'Malaysia'}
如果使用Element.findall()
或者Element.find()
方法,则只会从结点的直接子结点中查找,并不会递归查找。
for country in root.findall('country'):
rank = country.find('rank').text
name = country.get('name')
print name, rank
5、更新XML结点
如果需要更新结点的文本,可以通过直接修改Element.text
来实现。如果需要更新结点的属性,可以通过直接修改Element.attrib
来实现。 对结点进行更新后,可以使用ElementTree.write()
方法将更新后的 XML 文档写入文件中。
# 获取 XML 文档对象 ElementTree
tree = ET.parse('example.xml')
# 获取 XML 文档对象的根结点 Element
root = tree.getroot()
for rank in root.iter('rank'):
new_rank = int(rank.text) + 1
rank.text = str(new_rank)
rank.attrib['updated'] = 'yes'
tree.write('output.xml', encoding='UTF-8')
或者使用set设置对象的值,注意这里的set用于设置属性的值,而不是设置text的值。使用remove删除元素的值
tree = ET.parse("country_data.xml")
root = tree.getroot()
for rank in root.iter('rank'):
rank.text = str(int(rank.text) + 1)
rank.attrib['updatedrank'] = 'updated rank.com'
for year in root.iter('year'):
year.set('updatedyear','update year.com')
tree.write('update.xml')
for country in root.findall('country'):
gdppc = int(country.find('gdppc').text)
if gdppc < 50000:
root.remove(country)
tree.write('remove.xml')
update.xml为
<?xml version="1.0"?>
-<data>
-<country name="Liechtenstein">
<rank updatedrank="updated rank.com">2</rank>
<year updatedyear="update year.com">2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
-<country name="Singapore">
<rank updatedrank="updated rank.com">5</rank>
<year updatedyear="update year.com">2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
-<country name="Panama">
<rank updatedrank="updated rank.com">69</rank>
<year updatedyear="update year.com">2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
remove.xml
<?xml version="1.0"?>
-<data>
-<country name="Liechtenstein">
<rank updatedrank="updated rank.com">2</rank>
<year updatedyear="update year.com">2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
-<country name="Singapore">
<rank updatedrank="updated rank.com">5</rank>
<year updatedyear="update year.com">2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
</data>
使用append更新元素的值
#使用append更新
newEle0 = ET.Element('newBuildElement')
newEle1 = ET.Element('newBuildElement')
newEle2 = ET.Element('newBuildElement')
newEle0.set('name',"This is a new Element")
newEle1.text = "This is a new Element"
newEle1.attrib = {'name':"ren",'age':'26'}
newEle2.set("'name':'ren','age':'26'","This is a new Element")
root.append(newEle0)
root.append(newEle1)
root.append(newEle2)
tree.write('append.xml')
6.XML带有命名空间
<?xml version='1.0' encoding='UTF-8'?>
<nvd xmlns="namesapce test">
<data>
<country1 name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<tag1>temp</tag1>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country1>
<country2 name="中国">
<rank2>68</rank2>
<year2>2011</year2>
<gdppc2>13600</gdppc2>
<neighbor2 name="赵三" direction="西边"/>
<neighbor2 name="李四" direction="东北"/>
</country2>
</data>
</nvd>
import xml.etree.ElementTree as ET
# 获取 XML 文档对象 ElementTree
tree = ET.parse('websit.xml')
namsespceVar = "{namesapce test}"
for index in root.iter("neighbor"):
print "index.text", index.attrib
运行时会报错提示当前节点无此属性
AttributeError: 'NoneType' object has no attribute 'text'
在 XML 中,元素名称是由开发者定义的,当两个不同的文档使用相同的元素名时,就会发生命名冲突,而XML 命名空间提供避免元素命名冲突的方法。XML 命名空间属性被放置于元素的开始标签之中,并使用以下的语法:
xmlns:namespace-prefix="namespaceURI"
当命名空间被定义在元素的开始标签中时,所有带有相同前缀的子元素都会与同一个命名空间相关联。
注释:用于标示命名空间的地址不会被解析器用于查找信息。其惟一的作用是赋予命名空间一个惟一的名称。
除了显式定义,为元素定义默认的命名空间可以让我们省去在所有的子元素中使用前缀的工作。也就是说所有没有前缀的标签都会带有默认的命名空间,请使用下面的语法:
xmlns="namespaceURI"
如下所示
import xml.etree.ElementTree as ET
tree = ET.parse('websit.xml')
namsespceVar = "{namesapce test}"
root = tree.getroot()
# var = ET.tostring(root)
for index in root.iter(namsespceVar+"neighbor"):
index.text = "{}".format(u"修改后结果")
print "index.text", index.attrib
tree.write("countryResult.xml",encoding="UTF-8")
重新写入的结果展示如下:
但是发现每一个节点前面都加了ns0,那如何处理成节点前不要ns0的情形呢?
使用ET.register_namespace()函数
import xml.etree.ElementTree as ET
# 获取 XML 文档对象 ElementTree
tree = ET.parse('websit.xml')
namsespceVar = "{namesapce test}"
#注册命名空间
ET.register_namespace("","namesapce test")
# # 获取 XML 文档对象的根结点 Element
root = tree.getroot()
for index in root.iter(namsespceVar+"neighbor"):
index.text = "{}".format(u"修改后结果")
print "index.text", index.attrib
tree.write("countryResult.xml",encoding="UTF-8")
保存后发现解决了上述问题。
7.XML遍历与应用
一个XML文件,想要通过传递参数改变XML中结点的text值
如的xml文件中,想要把neighbor结点中的text值进行修改。
import xml.etree.ElementTree as ET
tree = ET.parse('websit.xml')
namsespceVar = "{namesapce test}"
ET.register_namespace("","namesapce test")
root = tree.getroot()
def chanage(kwargs={}):
for key in kwargs.keys():
for index in root.iter(namsespceVar+key):
index.text = "{}".format(kwargs[key])
tree.write("countryResult.xml",encoding="UTF-8")
if __name__ == '__main__':
key = {"neighbor":u"改正结果4"}
chanage(key)
修改后的xml文件为:
<?xml version='1.0' encoding='UTF-8'?>
<nvd xmlns="namesapce test">
<data>
<country1 name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<tag1>temp</tag1>
<neighbor direction="E" name="Austria">改正结果4</neighbor>
<neighbor direction="W" name="Switzerland">改正结果4</neighbor>
</country1>
<country2 name="中国">
<rank2>68</rank2>
<year2>2011</year2>
<gdppc2>13600</gdppc2>
<neighbor2 direction="西边" name="赵三" />
<neighbor2 direction="东北" name="李四" />
</country2>
</data>
</nvd>