xml在軟件領(lǐng)域用途非常廣泛,有名人曰:
“當(dāng) XML(擴(kuò)展標(biāo)記語(yǔ)言)于 1998 年 2 月被引入軟件工業(yè)界時(shí),它給整個(gè)行業(yè)帶來(lái)了一場(chǎng)風(fēng)暴。有史以來(lái)第一次,這個(gè)世界擁有了一種用來(lái)結(jié)構(gòu)化文檔和數(shù)據(jù)的通用且適應(yīng)性強(qiáng)的格式,它不僅僅可以用于 WEB,而且可以被用于任何地方?!?/p>
---《Designing With Web Standards Second Edition》, Jeffrey Zeldman
對(duì)于xml如果要做一個(gè)定義式的說(shuō)明,就不得不引用w3cschool里面簡(jiǎn)潔而明快的說(shuō)明:
如果讀者要詳細(xì)了解和學(xué)習(xí)有關(guān)xml,可以閱讀w3cschool的教程
xml的重要,關(guān)鍵在于它是用來(lái)傳輸數(shù)據(jù),因?yàn)閭鬏敂?shù)據(jù),特別是在web編程中,經(jīng)常要用到的。有了這樣一種東西,就讓數(shù)據(jù)傳輸變得簡(jiǎn)單了。對(duì)于這么重要的,python當(dāng)然有支持。
一般來(lái)講,一個(gè)引人關(guān)注的東西,總會(huì)有很多人從不同側(cè)面去關(guān)注。在編程語(yǔ)言中也是如此,所以,對(duì)xml這個(gè)明星式的東西,python提供了多種模塊來(lái)處理。
所以,我用xml.etree.ElementTree
ElementTree在標(biāo)準(zhǔn)庫(kù)中有兩種實(shí)現(xiàn)。一種是純Python實(shí)現(xiàn):xml.etree.ElementTree ,另外一種是速度快一點(diǎn):xml.etree.cElementTree 。
如果讀者使用的是python2.x,可以像這樣引入模塊:
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
如果是Python3.3以上,就沒有這個(gè)必要了,只需要一句話import xml.etree.ElementTree as ET
即可,然后由模塊自動(dòng)來(lái)尋找適合的方式。顯然python3.x相對(duì)python2.x有了很大進(jìn)步。但是,本教程礙于很多工程項(xiàng)目還沒有升級(jí)換代,暫且忍受了。
先要搞一個(gè)xml文檔。為了圖省事,我就用w3cschool中的一個(gè)例子:
這是一個(gè)xml樹,只不過是用圖來(lái)表示的,還沒有用ET解析呢。把這棵樹寫成xml文檔格式:
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
將xml保存為名為22601.xml的文件,然后對(duì)其進(jìn)行如下操作:
>>> import xml.etree.cElementTree as ET
為了簡(jiǎn)化,我用這種方式引入,如果在編程實(shí)踐中,推薦讀者使用try...except...方式。
>>> tree = ET.ElementTree(file="22601.xml")
>>> tree
<ElementTree object at 0xb724cc2c>
建立起xml解析樹。然后可以通過根節(jié)點(diǎn)向下開始讀取各個(gè)元素(element對(duì)象)。
在上述xml文檔中,根元素是,它沒有屬性,或者屬性為空。
>>> root = tree.getroot() #獲得根
>>> root.tag
'bookstore'
>>> root.attrib
{}
要想將根下面的元素都讀出來(lái),可以:
>>> for child in root:
... print child.tag, child.attrib
...
book {'category': 'COOKING'}
book {'category': 'CHILDREN'}
book {'category': 'WEB'}
也可以這樣讀取指定元素的信息:
>>> root[0].tag
'book'
>>> root[0].attrib
{'category': 'COOKING'}
>>> root[0].text #無(wú)內(nèi)容
'\n '
再深點(diǎn),就有感覺了:
>>> root[0][0].tag
'title'
>>> root[0][0].attrib
{'lang': 'en'}
>>> root[0][0].text
'Everyday Italian'
對(duì)于ElementTree對(duì)象,有一個(gè)iter方法可以對(duì)指定名稱的子節(jié)點(diǎn)進(jìn)行深度優(yōu)先遍歷。例如:
>>> for ele in tree.iter(tag="book"): #遍歷名稱為book的節(jié)點(diǎn)
... print ele.tag, ele.attrib
...
book {'category': 'COOKING'}
book {'category': 'CHILDREN'}
book {'category': 'WEB'}
>>> for ele in tree.iter(tag="title"): #遍歷名稱為title的節(jié)點(diǎn)
... print ele.tag, ele.attrib, ele.text
...
title {'lang': 'en'} Everyday Italian
title {'lang': 'en'} Harry Potter
title {'lang': 'en'} Learning XML
如果不指定元素名稱,就是將所有的元素遍歷一邊。
>>> for ele in tree.iter():
... print ele.tag, ele.attrib
...
bookstore {}
book {'category': 'COOKING'}
title {'lang': 'en'}
author {}
year {}
price {}
book {'category': 'CHILDREN'}
title {'lang': 'en'}
author {}
year {}
price {}
book {'category': 'WEB'}
title {'lang': 'en'}
author {}
year {}
price {}
除了上面的方法,還可以通過路徑,搜索到指定的元素,讀取其內(nèi)容。這就是xpath。此處對(duì)xpath不詳解,如果要了解可以到網(wǎng)上搜索有關(guān)信息。
>>> for ele in tree.iterfind("book/title"):
... print ele.text
...
Everyday Italian
Harry Potter
Learning XML
利用findall()方法,也可以是實(shí)現(xiàn)查找功能:
>>> for ele in tree.findall("book"):
... title = ele.find('title').text
... price = ele.find('price').text
... lang = ele.find('title').attrib
... print title, price, lang
...
Everyday Italian 30.00 {'lang': 'en'}
Harry Potter 29.99 {'lang': 'en'}
Learning XML 39.95 {'lang': 'en'}
除了讀取有關(guān)數(shù)據(jù)之外,還能對(duì)xml進(jìn)行編輯,即增刪改查功能。還是以上面的xml文檔為例:
>>> root[1].tag
'book'
>>> del root[1]
>>> for ele in root:
... print ele.tag
...
book
book
如此,成功刪除了一個(gè)節(jié)點(diǎn)。原來(lái)有三個(gè)book節(jié)點(diǎn),現(xiàn)在就還剩兩個(gè)了。打開源文件再看看,是不是正好少了第二個(gè)節(jié)點(diǎn)呢?一定很讓你失望,源文件居然沒有變化。
的確如此,源文件沒有變化,這就對(duì)了。因?yàn)橹链说男薷膭?dòng)作,還是停留在內(nèi)存中,還沒有將修改結(jié)果輸出到文件。不要忘記,我們是在內(nèi)存中建立的ElementTree對(duì)象。再這樣做:
>>> import os
>>> outpath = os.getcwd()
>>> file = outpath + "/22601.xml"
把當(dāng)前文件路徑拼裝好。然后:
>>> tree.write(file)
再看源文件,已經(jīng)變成兩個(gè)節(jié)點(diǎn)了。
除了刪除,也能夠修改:
>>> for price in root.iter("price"): #原來(lái)每本書的價(jià)格
... print price.text
...
30.00
39.95
>>> for price in root.iter("price"): #每本上漲7元,并且增加屬性標(biāo)記
... new_price = float(price.text) + 7
... price.text = str(new_price)
... price.set("updated","up")
...
>>> tree.write(file)
查看源文件:
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price updated="up">37.0</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price updated="up">46.95</price>
</book>
</bookstore>
不僅價(jià)格修改了,而且在price標(biāo)簽里面增加了屬性標(biāo)記。干得不錯(cuò)。
上面用del
來(lái)刪除某個(gè)元素,其實(shí),在編程中,這個(gè)用的不多,更喜歡用remove()方法。比如我要?jiǎng)h除price > 40
的書??梢赃@么做:
>>> for book in root.findall("book"):
... price = book.find("price").text
... if float(price) > 40.0:
... root.remove(book)
...
>>> tree.write(file)
于是就這樣了:
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price updated="up">37.0</price>
</book>
</bookstore>
接下來(lái)就要增加元素了。
>>> import xml.etree.cElementTree as ET
>>> tree = ET.ElementTree(file="22601.xml")
>>> root = tree.getroot()
>>> ET.SubElement(root, "book") #在root里面添加book節(jié)點(diǎn)
<Element 'book' at 0xb71c7578>
>>> for ele in root:
... print ele.tag
...
book
book
>>> b2 = root[1] #得到新增的book節(jié)點(diǎn)
>>> b2.text = "python" #添加內(nèi)容
>>> tree.write("22601.xml")
查看源文件:
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price updated="up">37.0</price>
</book>
<book>python</book>
</bookstore>
ET里面的屬性和方法不少,這里列出常用的,供使用中備查。
Element對(duì)象
常用屬性:
針對(duì)屬性的操作
針對(duì)后代的操作
ElementTree對(duì)象
最后,提供一個(gè)參考,這是一篇來(lái)自網(wǎng)絡(luò)的文章:Python xml屬性、節(jié)點(diǎn)、文本的增刪改,本文的源碼我也復(fù)制到下面,請(qǐng)讀者參考:
實(shí)現(xiàn)思想:
使用ElementTree,先將文件讀入,解析成樹,之后,根據(jù)路徑,可以定位到樹的每個(gè)節(jié)點(diǎn),再對(duì)節(jié)點(diǎn)進(jìn)行修改,最后直接將其輸出.
#!/usr/bin/python
# -*- coding=utf-8 -*-
# author : wklken@yeah.net
# date: 2012-05-25
# version: 0.1
from xml.etree.ElementTree import ElementTree,Element
def read_xml(in_path):
'''
讀取并解析xml文件
in_path: xml路徑
return: ElementTree
'''
tree = ElementTree()
tree.parse(in_path)
return tree
def write_xml(tree, out_path):
'''
將xml文件寫出
tree: xml樹
out_path: 寫出路徑
'''
tree.write(out_path, encoding="utf-8",xml_declaration=True)
def if_match(node, kv_map):
'''
判斷某個(gè)節(jié)點(diǎn)是否包含所有傳入?yún)?shù)屬性
node: 節(jié)點(diǎn)
kv_map: 屬性及屬性值組成的map
'''
for key in kv_map:
if node.get(key) != kv_map.get(key):
return False
return True
#---------------search -----
def find_nodes(tree, path):
'''
查找某個(gè)路徑匹配的所有節(jié)點(diǎn)
tree: xml樹
path: 節(jié)點(diǎn)路徑
'''
return tree.findall(path)
def get_node_by_keyvalue(nodelist, kv_map):
'''
根據(jù)屬性及屬性值定位符合的節(jié)點(diǎn),返回節(jié)點(diǎn)
nodelist: 節(jié)點(diǎn)列表
kv_map: 匹配屬性及屬性值map
'''
result_nodes = []
for node in nodelist:
if if_match(node, kv_map):
result_nodes.append(node)
return result_nodes
#---------------change -----
def change_node_properties(nodelist, kv_map, is_delete=False):
'''
修改/增加 /刪除 節(jié)點(diǎn)的屬性及屬性值
nodelist: 節(jié)點(diǎn)列表
kv_map:屬性及屬性值map
'''
for node in nodelist:
for key in kv_map:
if is_delete:
if key in node.attrib:
del node.attrib[key]
else:
node.set(key, kv_map.get(key))
def change_node_text(nodelist, text, is_add=False, is_delete=False):
'''
改變/增加/刪除一個(gè)節(jié)點(diǎn)的文本
nodelist:節(jié)點(diǎn)列表
text : 更新后的文本
'''
for node in nodelist:
if is_add:
node.text += text
elif is_delete:
node.text = ""
else:
node.text = text
def create_node(tag, property_map, content):
'''
新造一個(gè)節(jié)點(diǎn)
tag:節(jié)點(diǎn)標(biāo)簽
property_map:屬性及屬性值map
content: 節(jié)點(diǎn)閉合標(biāo)簽里的文本內(nèi)容
return 新節(jié)點(diǎn)
'''
element = Element(tag, property_map)
element.text = content
return element
def add_child_node(nodelist, element):
'''
給一個(gè)節(jié)點(diǎn)添加子節(jié)點(diǎn)
nodelist: 節(jié)點(diǎn)列表
element: 子節(jié)點(diǎn)
'''
for node in nodelist:
node.append(element)
def del_node_by_tagkeyvalue(nodelist, tag, kv_map):
'''
同過屬性及屬性值定位一個(gè)節(jié)點(diǎn),并刪除之
nodelist: 父節(jié)點(diǎn)列表
tag:子節(jié)點(diǎn)標(biāo)簽
kv_map: 屬性及屬性值列表
'''
for parent_node in nodelist:
children = parent_node.getchildren()
for child in children:
if child.tag == tag and if_match(child, kv_map):
parent_node.remove(child)
if __name__ == "__main__":
#1\. 讀取xml文件
tree = read_xml("./test.xml")
#2\. 屬性修改
#A. 找到父節(jié)點(diǎn)
nodes = find_nodes(tree, "processers/processer")
#B. 通過屬性準(zhǔn)確定位子節(jié)點(diǎn)
result_nodes = get_node_by_keyvalue(nodes, {"name":"BProcesser"})
#C. 修改節(jié)點(diǎn)屬性
change_node_properties(result_nodes, {"age": "1"})
#D. 刪除節(jié)點(diǎn)屬性
change_node_properties(result_nodes, {"value":""}, True)
#3\. 節(jié)點(diǎn)修改
#A.新建節(jié)點(diǎn)
a = create_node("person", {"age":"15","money":"200000"}, "this is the firest content")
#B.插入到父節(jié)點(diǎn)之下
add_child_node(result_nodes, a)
#4\. 刪除節(jié)點(diǎn)
#定位父節(jié)點(diǎn)
del_parent_nodes = find_nodes(tree, "processers/services/service")
#準(zhǔn)確定位子節(jié)點(diǎn)并刪除之
target_del_node = del_node_by_tagkeyvalue(del_parent_nodes, "chain", {"sequency" : "chain1"})
#5\. 修改節(jié)點(diǎn)文本
#定位節(jié)點(diǎn)
text_nodes = get_node_by_keyvalue(find_nodes(tree, "processers/services/service/chain"), {"sequency":"chain3"})
change_node_text(text_nodes, "new text")
#6\. 輸出到結(jié)果文件
write_xml(tree, "./out.xml")
操作對(duì)象(原始xml文件):
<?xml version="1.0" encoding="UTF-8"?>
<framework>
<processers>
<processer name="AProcesser" file="lib64/A.so"
path="/tmp">
</processer>
<processer name="BProcesser" file="lib64/B.so" value="fordelete">
</processer>
<processer name="BProcesser" file="lib64/B.so2222222"/>
<services>
<service name="search" prefix="/bin/search?"
output_formatter="OutPutFormatter:service_inc">
<chain sequency="chain1"/>
<chain sequency="chain2"></chain>
</service>
<service name="update" prefix="/bin/update?">
<chain sequency="chain3" value="fordelete"/>
</service>
</services>
</processers>
</framework>
執(zhí)行程序之后,得到的結(jié)果文件:
<?xml version='1.0' encoding='utf-8'?>
<framework>
<processers>
<processer file="lib64/A.so" name="AProcesser" path="/tmp">
</processer>
<processer age="1" file="lib64/B.so" name="BProcesser">
<person age="15" money="200000">this is the firest content</person>
</processer>
<processer age="1" file="lib64/B.so2222222" name="BProcesser">
<person age="15" money="200000">this is the firest content</person>
</processer>
<services>
<service name="search" output_formatter="OutPutFormatter:service_inc"
prefix="/bin/search?">
<chain sequency="chain2" />
</service>
<service name="update" prefix="/bin/update?">
<chain sequency="chain3" value="fordelete">new text</chain>
</service>
</services>
</processers>
</framework>
更多建議: