Python中使用ElementTree對(duì)XML文件進(jìn)行解析
官方API介紹:https://docs.python.org/3.6/library/xml.etree.elementtree.html
XML文件介紹:
XML指可擴(kuò)展標(biāo)記語(yǔ)言(eXtensible Markup Language),接觸過(guò)Java或者Android的同學(xué)們相信絕對(duì)不會(huì)陌生,XML被設(shè)計(jì)用來(lái)傳輸和存儲(chǔ)數(shù)據(jù),雖然現(xiàn)在用來(lái)與服務(wù)端交互更多情況下使用的都是Json格式的數(shù)據(jù),但是XML格式還是有著廣泛的應(yīng)用。
最近在公司的項(xiàng)目中,需要使用Python腳本在后臺(tái)對(duì)反編譯的apk文件進(jìn)行合并,其中對(duì)Manifest文件中標(biāo)簽的處理,主要就是用到了Python中的XML解析,因此寫(xiě)一篇文章記錄一下。
Python中提供的XML解析方式:
| 方法 | 特點(diǎn) |
|---|---|
| SAX | SAX解析通過(guò)流模式在解析XML的過(guò)程中觸發(fā)對(duì)應(yīng)的事件(start_element、char_data、end_element)并調(diào)用用戶定義的回調(diào)函數(shù)來(lái)處理XML文件。 |
| DOM | 將XML數(shù)據(jù)在內(nèi)存中解析成一個(gè)樹(shù),通過(guò)對(duì)樹(shù)的操作來(lái)操作XML,占用內(nèi)存大,解析速度較慢,優(yōu)點(diǎn)是可以任意遍歷樹(shù)的節(jié)點(diǎn)。 |
| ElementTree | 類(lèi)似一個(gè)輕量級(jí)的DOM,也是本篇文章主要介紹的。 |
準(zhǔn)備一份XML格式的文件:
巧婦難為無(wú)米之炊,需要進(jìn)行解析,首先必須有一個(gè)XML文件,本篇文章采用官方API示例中的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>
XML文件格式介紹:
<tag attrib = > text </tag> tail
例:<APP_KEY channel = 'CSDN'> hello123456789 </APP_KEY>
- tag,即標(biāo)簽,用于標(biāo)識(shí)該元素表示哪種數(shù)據(jù),即APP_KEY
- attrib,即屬性,用Dictionary形式保存,即{'channel' = 'CSDN'}
- text,文本字符串,可以用來(lái)存儲(chǔ)一些數(shù)據(jù),即hello123456789
- tail,尾字符串,并不是必須的,例子中沒(méi)有包含。
ElementTree解析XML文件的過(guò)程:
- 導(dǎo)入ElementTree,
import xml.etree.ElementTree as ET - 解析Xml文件找到根節(jié)點(diǎn):
- 直接解析XML文件并獲得根節(jié)點(diǎn),
tree = ET.parse('country_data.xml') root = tree.getroot() - 解析字符串,
root = ET.fromstring(country_data_as_string) - 遍歷根節(jié)點(diǎn)可以獲得子節(jié)點(diǎn),然后就可以根據(jù)需求拿到需要的字段了。
源碼演示:
示例代碼:
#!usr/bin/python
# -*- coding: utf-8 -*-
#==========================
import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()
print('root-tag:',root.tag,',root-attrib:',root.attrib,',root-text:',root.text)
for child in root:
print('child-tag是:',child.tag,',child.attrib:',child.attrib,',child.text:',child.text)
for sub in child:
print('sub-tag是:',sub.tag,',sub.attrib:',sub.attrib,',sub.text:',sub.text)
運(yùn)行結(jié)果:
>>>
root-tag: data ,root-attrib: {} ,root-text:
child-tag是: country ,child.attrib: {'name': 'Liechtenstein'} ,child.text:
sub-tag是: rank ,sub.attrib: {} ,sub.text: 1
sub-tag是: year ,sub.attrib: {} ,sub.text: 2008
sub-tag是: gdppc ,sub.attrib: {} ,sub.text: 141100
sub-tag是: neighbor ,sub.attrib: {'direction': 'E', 'name': 'Austria'} ,sub.text: None
sub-tag是: neighbor ,sub.attrib: {'direction': 'W', 'name': 'Switzerland'} ,sub.text: None
child-tag是: country ,child.attrib: {'name': 'Singapore'} ,child.text:
sub-tag是: rank ,sub.attrib: {} ,sub.text: 4
sub-tag是: year ,sub.attrib: {} ,sub.text: 2011
sub-tag是: gdppc ,sub.attrib: {} ,sub.text: 59900
sub-tag是: neighbor ,sub.attrib: {'direction': 'N', 'name': 'Malaysia'} ,sub.text: None
child-tag是: country ,child.attrib: {'name': 'Panama'} ,child.text:
sub-tag是: rank ,sub.attrib: {} ,sub.text: 68
sub-tag是: year ,sub.attrib: {} ,sub.text: 2011
sub-tag是: gdppc ,sub.attrib: {} ,sub.text: 13600
sub-tag是: neighbor ,sub.attrib: {'direction': 'W', 'name': 'Costa Rica'} ,sub.text: None
sub-tag是: neighbor ,sub.attrib: {'direction': 'E', 'name': 'Colombia'} ,sub.text: None
查找指定的子節(jié)點(diǎn):
當(dāng)XML文件較大或者其中的子節(jié)點(diǎn)tag非常多的時(shí)候,一個(gè)一個(gè)獲取是比較麻煩的而且有很多不是我們需要的,這樣我們可以通過(guò)find('nodeName')或者findall('nodeName')方法來(lái)查找指定tag的節(jié)點(diǎn)。
- find('nodeName'):表示在該節(jié)點(diǎn)下,查找其中第一個(gè)tag為nodeName的節(jié)點(diǎn)。
- findall('nodeName'):表示在該節(jié)點(diǎn)下,查找其中所有tag為nodeName的節(jié)點(diǎn)。
代碼示例:
#!usr/bin/python
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()
animNode = root.find('country') #查找root節(jié)點(diǎn)下第一個(gè)tag為country的節(jié)點(diǎn)
print(animNode.tag,animNode.attrib,animNode.text)
運(yùn)行結(jié)果,可以在XML文件中看到,第一個(gè)tag為country的節(jié)點(diǎn)確實(shí)是Liechtenstein:
>>>
country {'name': 'Liechtenstein'}
刪除指定的節(jié)點(diǎn)以及保存
在合并Manifest文件的時(shí)候,可能有一些重復(fù)的權(quán)限,那么怎么刪除掉呢,刪除指定節(jié)點(diǎn)可以通過(guò)remove(node)方法,移除指定的節(jié)點(diǎn)。
代碼示例,比如我們想移除attribute中name為L(zhǎng)iechtenstein的節(jié)點(diǎn):
#!usr/bin/python
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()
animNode = root.find('country')
if animNode.attrib['name'] == 'Liechtenstein':
root.remove(animNode)
tree.write('finish.xml')#保存修改后的XML文件
運(yùn)行結(jié)果,我們打開(kāi)保存的finish.xml文件,發(fā)現(xiàn)保存結(jié)果如下,name為L(zhǎng)iechtenstein的節(jié)點(diǎn)已經(jīng)被刪除了:
<data>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor direction="N" name="Malaysia" />
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>