1. 程式人生 > >Python 標準庫之XML

Python 標準庫之XML

寫在之前

帶分隔符的檔案僅有兩維的資料:行 & 列。如果我們想在程式之間交換資料結構,需要一種方法把層次結構,序列,集合和其它的資料結構編碼成文字。

今天要說的 XML 是最突出的處理上述這種轉換的標記格式,它使用標籤(tag)分隔資料。XML 在軟體領域的用途非常廣泛。

XML

XML 是什麼?如果非要對其做一個定義式的說明,那這裡我不得不引用一下 w3school 裡面簡潔而明快的說明:

XML 指可擴充套件標記語言(EXtensible Markup Language);

XML 是一種標記語言,類似於 HTML;

XML 的設計宗旨是傳輸資料,而非顯示資料;

XML 標籤沒有被預定義,需要自行定義標籤;

XML 被設計為具有自我描述性;

XML 是 W3C 的推薦標準。

如果你想要詳細瞭解和學習 XML 的話,可以去閱讀 w3school 的 XML 教程即可,裡面講述的很詳細,在下面我還會引用一些裡面的內容。

XML 的重要性在於它是用來傳輸資料的,因此,特別是在 Web 程式設計中我們經常會用到它。有了它,讓資料傳輸變的更加簡單,這麼重要的東西,我大 Python 當然支援。

有大佬曾經說過:“一個引人關注的東西總會有很多人從不同側面去研究它”。這個在程式設計中也同樣適用,所以對於 XML 這個紅得發紫的東西,Python 提供了多種模組來處理。

  • xml.dom.* 模組:Document Object Model。適合用於處理 DOM API。它能夠將 XML 資料在記憶體中解析成一個樹,然後通過對樹的操作來操作 XML。但是這種方式由於將 XML 資料對映到記憶體中的樹,導致比較慢,且消耗更多記憶體。

  • xml.sax.* 模組:simple API for XML。由於 SAX 以流式讀取 XML 檔案,從而速度較快,佔用記憶體少,但是在操作上稍微複雜,需要使用者實現回撥函式。

當然還有一些別的,比如 xml.parse.expat,xml.etree.ElementTree 等等,我就不在列舉了,碰到的時候再去查查,否則光看這些東西頭就大了,而且無聊的很。

遍歷查詢

先要做一個 XML 文件,我自己想也想不出個啥太好的來,所以直接用 w3school 中的一個例子,如下圖所示:
在這裡插入圖片描述
上圖表示下面的 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 儲存並且命名為 test.xml 檔案,接下來就是以它為物件,練習各種操作了。

>>> import xml.etree.ElementTree as ET
>>> tree = ET.ElementTree(file = 'test.xml')
>>> tree
<xml.etree.ElementTree.ElementTree object at 0x00000000025B8630>

上面建立起 XML 解析樹物件,然後通過根節點向下開始讀取各個元素(element 物件)。

在上述 XML 文件中,根元素是 bookstore,它沒有屬性,也可以說是屬性為空。

>>> root = tree.getroot()
>>> root.tag
'bookstore'
>>> root.attrib
{}

要想將根下面的元素都讀取出來,可以進行如下操作:

>>> 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
'\n  '

上述的 root[0].text 無內容,再深入一層,我們就可以看到內容了:

>>> root[0][0].tag
'title'
>>> root[0][0].attrib
{'lang': 'en'}
>>> root[0][0].text
'Everyday Italian'

對於 ElementTree 物件,有一個 iter() 方法可以對指定名稱的子節點進行深度優先遍歷,例如下面這樣:

>>> for ele in tree.iter(tag='book'):
...    print(ele.tag,ele.attrib)
...
('book', {'category': 'COOKING'})
('book', {'category': 'CHILDREN'})
('book', {'category': 'WEB'})

上述程式碼是遍歷名稱為 book 的節點,如果不指定節點的話,就是將所有的元素遍歷一遍:

>>> 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', {})

除了上面的方法外,還可以通過路徑搜尋到指定的元素,然後讀取其內容,這就是 xpath,關於 xpath 是什麼,在這不多做介紹,感興趣的可以去 Google。

編輯(增刪改查)

我們還是用上面的例子,為了方便檢視,我把內容再貼上過來,下面的內容記得儲存並且命名為 test.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 進行了讀取的有關操作,其實還可以對 XML 進行編輯,也就是增刪改查的功能,下面我們來操作一下:

>>> import xml.etree.ElementTree as ET
>>> tree = ET.ElementTree(file = "test.xml")
>>> root = tree.getroot() #獲得根
>>> root[1].tag
'book'
>>> del root[1]
>>> for ele in root:
...    print(ele.tag)
...
book
book

如上,我們成功的刪除了一個節點,原來有 3 個 book 節點,現在就只剩下兩個了。接下來讓我們開啟原始檔看看,是不是正好缺少了第 2 個節點呢?結果讓我們很失望,原始檔並沒有什麼變化。

確實如此,原始檔並沒有變,因為到了這一步的修改動作還只是停留在記憶體裡,還沒有將修改的結果輸出到檔案,不要忘記我們是在記憶體中建立的 ElementTree 物件。那麼該如何做呢?請接著往下看:

>>> import os
>>> outpath = os.getcwd()
>>> file = outpath + "/test.xml"

把當前檔案的路徑拼裝好。

>>> tree.write(file)

做完上面的操作以後再去看原始檔,已經變成兩個節點了。

除了刪除,也是可以修改的:

>>> for price in root.iter('price'): #原來每本書的價格
...    print(price.text)
...
30.00
39.95
>>> for price in root.iter('price'): #每本上漲 10 元並做標記
...    new_price = float(price.text) + 10
...    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">50.0</price> 
</book>
<book category="WEB">
 <title lang="en">Learning XML</title> 
 <author>Erik T. Ray</author> 
 <year>2003</year> 
 <price updated="up">49.95</price> 
</book>
</bookstore>

通過對比我們可以發現,不僅價格改變了,而且在 price 標籤裡面增加了屬性標記。

上面我們是用 del 來刪除某個元素,其實這個在程式設計中我們用的並不多,一般情況下更喜歡用 remove() 方法。比如要刪除 price = 50 的書,可以像下面這樣操作:

>>> tree.write(file)
>>> for book in root.findall("book"):
...    price = book.find("price").text
...    if float(price) == 50:
...            root.remove(book)
...
>>> tree.write(file)

於是就有了下面的結果:

<bookstore>
<book category="WEB">
 <title lang="en">Learning XML</title> 
 <author>Erik T. Ray</author> 
 <year>2003</year> 
 <price updated="up">49.95</price> 
</book>
</bookstore>

接下來我們來看看增加元素:

>>> import xml.etree.ElementTree as ET
>>> tree = ET.ElementTree(file = 'test.xml')
>>> root = tree.getroot()
>>> ET.SubElement(root,"book") # 在root裡面新增book節點
<Element 'book' at 0x000000000209C778>
>>> for ele in root:
...     print(ele.tag)
...
book
book
>>> b2 = root[1]
>>> b2.text = 'python'
>>> tree.write('test.xml')

這樣就大功告成了,然後再像上面一樣看一下原始檔,發現果真增加了。

常用的屬性 & 方法

ET 裡面的屬性 & 方法很多,這裡列出常用的幾個,供使用中備查。

1.Element 物件

常用的屬性如下:

  • tag:string,元素資料種類
  • text:string,元素的內容
  • attrib:dictionary,元素的屬性字典
  • tail:string,元素的尾形

針對屬性的操作如下:

  • clear():清空元素的後代,屬性,text 和 tail 也設定為 None。
  • items():根據屬性字典返回一個列表,列表元素為(key,value)。
  • keys():返回包含所有元素屬性鍵的列表。
  • set(key,value):設定新的屬性鍵和值。

針對後代的操作如下:

  • append(subelement):新增直系子元素。
  • extend(sunelements):增加一串元素物件作為子元素。
  • find(match):尋找第一個匹配子元素,匹配物件可以為 tag 或 path。
  • findall(match):尋找所有匹配子元素,匹配物件可以為 tag 或 path。
  • insert(index,element):在指定位置插入子元素。
  • remove(subelement):刪除子元素

2.ElementTree 物件

  • find(match)。
  • findall(match)。
  • getroot():獲取根結點。
  • parse(source,parser = None):裝載 XML 物件,source 可以為檔名或檔案型別物件。

寫在之後

更多內容,歡迎關注公眾號「Python空間」,期待和你的交流。
在這裡插入圖片描述