ElementTree XML 模块用来处理 XML 格式文档。包括读取、解析、修改、生成等等。
xml.etree.ElementTree 库里主要使用的是两个类:
ElementTree 是对整个文档树(document tree)的抽象,操作文件、文档时需要。比如读取、写入、查找。
Element 是对文档中单个节点(document node)的抽象,操作单个节点时需要。比如修改属性、查找子节点。
读入 xml 文件
有两种途径,各自得到不同的结果。
返回 xml 树
使用库函数: xml.etree.ElementTree
库内的 parse()
函数。
返回的是一个 ElementTree 树对象
>>> from xml.etree.ElementTree import parse
>>> parse('a.xml')
<xml.etree.ElementTree.ElementTree object at 0x0000020DEAF736D0>
>>>
如果想得到树的根节点,还需要使用 getroot()
函数:
>>> from xml.etree.ElementTree import parse
>>> parse('a.xml').getroot()
<Element 'xml' at 0x0000020DEB462360>
>>>
返回 xml 根节点
使用类方法: xml.etree.ElementTree
库内的 ElementTree
类的实例方法。
可以读入文件名或者文件对象。
例子:从文件名读入:
>>> from xml.etree.ElementTree import ElementTree
>>> ElementTree().parse('a.xml')
<Element 'xml' at 0x0000020DEB462310>
可以看出,返回的是 Element
对象,这个对象是文档树的根。
例子:从文件对象读入
>>> with open('a.xml','r') as x:
... ElementTree().parse(x)
...
<Element 'xml' at 0x0000020DEB462400>
>>>
从字符串读入
使用库函数: xml.etree.ElementTree
库内的 fromstring()
函数。
>>> import xml.etree.ElementTree as ET
>>> ET.fromstring('<xml></xml>')
<Element 'xml' at 0x000001FD319BDEA0>
>>>
返回根节点。
输出字符串
使用类方法:xml.etree.ElementTree
库内的 dump()
和 tostring()
函数。
dump()
向标准输出打印出字符串,函数不返回字符串,仅用于调试。参数是节点对象 Element。
>>> import xml.etree.ElementTree as ET
>>> t=ET.fromstring('<xml><a>1</a></xml>')
>>> ET.dump(t)
<xml><a>1</a></xml>
>>>
tostring()
用来返回格式化后的 xml字符串,参数列表有:
tostring(element,
encoding='us-ascii',
method='xml',
*
xml_declaration=None,
default_namespace=None,
short_empty_elements=True)
参数中,element 是个 Element 节点对象。例子:
>>> import xml.etree.ElementTree as ET
>>> t=ET.fromstring('<xml><a>1</a></xml>')
>>> s=ET.tostring(t)
>>> s
b'<xml><a>1</a></xml>'
>>>
得到字符串后,就可以编写代码输出到文件。
encoding
参数指定编码,默认是 ascii 的字节编码。如果要输出 UTF-8 编码,可以赋值:“unicode”。例子:
>>> import xml.etree.ElementTree as ET
>>> t=ET.fromstring('<xml><a>1</a></xml>')
>>> s=ET.tostring(t, encoding="unicode")
>>> s
'<xml><a>1</a></xml>'
>>>
xml_declaration
参数控制是否同时输出xml 格式声明。例子:
>>> import xml.etree.ElementTree as ET
>>> t=ET.fromstring('<xml><a>1</a></xml>')
>>> s=ET.tostring(t, encoding="unicode", xml_declaration=True)
>>> s
"<?xml version='1.0' encoding='cp936'?>\n<xml><a>1</a></xml>"
>>> s=ET.tostring(t, xml_declaration=True)
>>> s
b"<?xml version='1.0' encoding='us-ascii'?>\n<xml><a>1</a></xml>"
>>>
或者直接使用 XML 树对象自带的方法:write()
,在下节介绍。
写入 xml 文件
函数的参数有:
write(file,
encoding='us-ascii',
xml_declaration=None,
default_namespace=None,
method='xml',
*,
short_empty_elements=True)
file
: 和 读入 xml 文件 一样,可以提供文件名或者文件对象。
其他参数和 tostring()
相同。
处理 xml 命名空间
声明命名空间的方法是声明一个 prefix 到 uri 的映射:
xmlns:prefix=uri
这里 prefix 是个临时替代字符串,用于替换 uri。解析 xml 时,所有tag 前面的 prefix 会被自动替换为 uri
如果没有 prefix,而采用下面形式
xmlns:uri
就是默认命名空间,default namespace,所有没有 prefix 的前缀会被自动替换为 uri
如果原始的 xml 声明了命名空间,在解析的时候会自动将扩展tag。
借用 这里的例子 说明命名空间的处理。
下面一个 xml 文件里,同时声明了 html 类型的 table 和家具类型的 table, 然后用命名空间区分。
<?xml version='1.0'?>
<root xmlns:h="html"
xmlns:f="furniture"
xmlns="nothing">
<h:table>
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table>
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>
上面文件的例子中,default namespace 声明为 nothing,另外声明了两种 namespace 分别为:
h="html"
和
f="furniture"
所有 h:
开头的tag都会被替换为 html:
,比如h:table
会被替换为html:table
。同样道理,f:table
会被替换为furniture:table
。而没有前缀的
扩展版本的 xml如下:
<?xml version='1.0'?>
<nothing:root xmlns:h="html"
xmlns:f="furniture"
xmlns="nothing">
<html:table>
<html:tr>
<html:td>Apples</html:td>
<html:td>Bananas</html:td>
</html:tr>
</html:table>
<furniture:table>
<furniture:name>African Coffee Table</furniture:name>
<furniture:width>80</furniture:width>
<furniture:length>120</furniture:length>
</furniture:table>
</nothing:root>
因为 prefix 是中间临时替代品,所以更改 prefix 并不会影响最终扩展出的xml。经过 ElementTree.parse() 后会统一替换 prefix 为统一编号的 ns
,从ns0
、ns1
……
>>> import xml.etree.ElementTree as ET
>>> a=ET.ElementTree()
>>> a.parse('a.xml')
>>> ET.dump(a.getroot())
<ns0:root xmlns:ns0="nothing" xmlns:ns1="html" xmlns:ns2="furniture">
<ns1:table>
<ns1:tr>
<ns1:td>Apples</ns1:td>
<ns1:td>Bananas</ns1:td>
</ns1:tr>
</ns1:table>
<ns2:table>
<ns2:name>African Coffee Table</ns2:name>
<ns2:width>80</ns2:width>
<ns2:length>120</ns2:length>
</ns2:table>
</ns0:root>
当我们查看元素 tag 的名称时,会看到放置在大括号中的 uri:
>>> r=a.getroot()
>>> r.tag
'{nothing}root'
当用 find()
寻找子元素时,需要提供扩展版的 tag 名称, 如下例子:
>>> r.find('{html}table')
<Element '{html}table' at 0x000002608E572720>
这样并不方便,应该使用 find()
函数的第二个参数:ns
,来指定命名空间,形式是从 prefix 到 uri 的映射。比如:
>>> r.find('h:table', {'h':'html'})
<Element '{html}table' at 0x000002608E572720>
格式化
默认情况下,tostring()
和 write()
的结果将保留原始文件的缩进格式,不会对空行、缩进做统一的格式化。
比如:如果原文件是这样:
<?xml version='1.0'?>
<root xmlns:h="html" xmlns:f="furniture" xmlns="nothing">
<h:table><h:tr><h:td>Apples</h:td><h:td>Bananas</h:td></h:tr></h:table>
<f:table><f:name>African Coffee Table</f:name><f:width>80</f:width><f:length>120</f:length></f:table>
</root>
那么经 ElementTree.parse() 处理后再输出是这样:
>>> import xml.etree.ElementTree as ET
>>> a=ET.ElementTree()
>>> a.parse('a.xml')
<Element '{nothing}root' at 0x0000020A5B822310>
>>> ET.dump(a.getroot())
<ns0:root xmlns:ns0="nothing" xmlns:ns1="html" xmlns:ns2="furniture">
<ns1:table><ns1:tr><ns1:td>Apples</ns1:td><ns1:td>Bananas</ns1:td></ns1:tr></ns1:table>
<ns2:table><ns2:name>African Coffee Table</ns2:name><ns2:width>80</ns2:width><ns2:length>120</ns2:length></ns2:table>
</ns0:root>
>>>
如果想得到如下的两种格式化,就要使用 ElementTree.indent()
函数,这个函数将整棵 xml 文档树重新整理,参数就是文档树的对象 ElementTree
。还是上面的例子:
>>> import xml.etree.ElementTree as ET
>>> a=ET.ElementTree()
>>> a.parse('a.xml')
<Element '{nothing}root' at 0x0000020A5B822310>
>>> ET.indent(a)
>>> ET.dump(a.getroot())
<ns0:root xmlns:ns0="nothing" xmlns:ns1="html" xmlns:ns2="furniture">
<ns1:table>
<ns1:tr>
<ns1:td>Apples</ns1:td>
<ns1:td>Bananas</ns1:td>
</ns1:tr>
</ns1:table>
<ns2:table>
<ns2:name>African Coffee Table</ns2:name>
<ns2:width>80</ns2:width>
<ns2:length>120</ns2:length>
</ns2:table>
</ns0:root>
>>>
indent()
做了两件事情:
- 每个节点独占一行
- 每一级节点增加一级缩进
如果您对本文有疑问或者寻求合作,欢迎 联系邮箱 。邮箱已到剪贴板
精彩评论
本站 是个人网站,采用 署名协议 CC-BY-NC 授权。
欢迎转载,请保留原文链接 https://www.lfhacks.com/tech/python-element-tree/ ,且不得用于商业用途。