XML 樹結構,語法規則,元素,屬性,驗證及其解析


XML 文檔形成了一種樹結構,它從"根部"開始,然后擴展到"枝葉"。


一個 XML 文檔實例

XML 文檔使用簡單的具有自我描述性的語法:

<?xml version="1.0" encoding="UTF-8"?> <note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note>

第一行是 XML 聲明。它定義 XML 的版本(1.0)和所使用的編碼(UTF-8 : 萬國碼, 可顯示各種語言)。

下一行描述文檔的根元素(像在說:"本文檔是一個便簽"):

<note>

接下來 4 行描述根的 4 個子元素(to, from, heading 以及 body):

<to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body>

最后一行定義根元素的結尾:

</note>

您可以假設,從這個實例中,XML 文檔包含了一張 Jani 寫給 Tove 的便簽。

XML 具有出色的自我描述性,您同意嗎?


XML 文檔形成一種樹結構

XML 文檔必須包含根元素。該元素是所有其他元素的父元素。

XML 文檔中的元素形成了一棵文檔樹。這棵樹從根部開始,並擴展到樹的最底端。

所有的元素都可以有子元素:

<root> <child> <subchild>.....</subchild> </child> </root>

父、子以及同胞等術語用於描述元素之間的關系。父元素擁有子元素。相同層級上的子元素成為同胞(兄弟或姐妹)。

所有的元素都可以有文本內容和屬性(類似 HTML 中)。


實例:

DOM node tree

上圖表示下面的 XML 中的一本書:

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>

實例中的根元素是 <bookstore>。文檔中的所有 <book> 元素都被包含在 <bookstore> 中。

<book> 元素有 4 個子元素:<title>、<author>、<year>、<price>。

XML 語法規則


XML 的語法規則很簡單,且很有邏輯。這些規則很容易學習,也很容易使用。


XML 文檔必須有根元素

XML 必須包含根元素,它是所有其他元素的父元素,比如以下實例中 root 就是根元素:

<root> <child> <subchild>.....</subchild> </child> </root>

以下實例中 note 是根元素:

<?xml version="1.0" encoding="UTF-8"?> <note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note>

XML 聲明

XML 聲明文件的可選部分,如果存在需要放在文檔的第一行,如下所示:

<?xml version="1.0" encoding="utf-8"?>

以上實例包含 XML 版本(<version="1.0"),甚至包含字符編碼(encoding="utf-8")。< p="">

UTF-8 也是 HTML5, CSS, JavaScript, PHP, 和 SQL 的默認編碼。


所有的 XML 元素都必須有一個關閉標簽

在 HTML 中,某些元素不必有一個關閉標簽:

<p>This is a paragraph. <br>

在 XML 中,省略關閉標簽是非法的。所有元素都必須有關閉標簽:

<p>This is a paragraph.</p> <br />

注釋:從上面的實例中,您也許已經注意到 XML 聲明沒有關閉標簽。這不是錯誤。聲明不是 XML 文檔本身的一部分,它沒有關閉標簽。


XML 標簽對大小寫敏感

XML 標簽對大小寫敏感。標簽 <Letter> 與標簽 <letter> 是不同的。

必須使用相同的大小寫來編寫打開標簽和關閉標簽:

<Message>這是錯誤的</message> <message>這是正確的</message>

注釋:打開標簽和關閉標簽通常被稱為開始標簽和結束標簽。不論您喜歡哪種術語,它們的概念都是相同的。


XML 必須正確嵌套

在 HTML 中,常會看到沒有正確嵌套的元素:

<b><i>This text is bold and italic</b></i>

在 XML 中,所有元素都必須彼此正確地嵌套:

<b><i>This text is bold and italic</i></b>

在上面的實例中,正確嵌套的意思是:由於 <i> 元素是在 <b> 元素內打開的,那么它必須在 <b> 元素內關閉。



XML 屬性值必須加引號

與 HTML 類似,XML 元素也可擁有屬性(名稱/值的對)。

在 XML 中,XML 的屬性值必須加引號。

請研究下面的兩個 XML 文檔。 第一個是錯誤的,第二個是正確的:

<note date=12/11/2007> <to>Tove</to> <from>Jani</from> </note>
<note date="12/11/2007"> <to>Tove</to> <from>Jani</from> </note>

在第一個文檔中的錯誤是,note 元素中的 date 屬性沒有加引號。


實體引用

在 XML 中,一些字符擁有特殊的意義。

如果您把字符 "<" 放在 XML 元素中,會發生錯誤,這是因為解析器會把它當作新元素的開始。

這樣會產生 XML 錯誤:

<message>if salary < 1000 then</message>

為了避免這個錯誤,請用實體引用來代替 "<" 字符:

<message>if salary &lt; 1000 then</message>

在 XML 中,有 5 個預定義的實體引用:

&lt; < less than
&gt; > greater than
&amp; & ampersand
&apos; ' apostrophe
&quot; " quotation mark

注釋:在 XML 中,只有字符 "<" 和 "&" 確實是非法的。大於號是合法的,但是用實體引用來代替它是一個好習慣。


XML 中的注釋

在 XML 中編寫注釋的語法與 HTML 的語法很相似。

<!-- This is a comment -->

在 XML 中,空格會被保留

HTML 會把多個連續的空格字符裁減(合並)為一個:

HTML: Hello Tove
Output: Hello Tove

在 XML 中,文檔中的空格不會被刪減。


XML 以 LF 存儲換行

在 Windows 應用程序中,換行通常以一對字符來存儲:回車符(CR)和換行符(LF)。

在 Unix 和 Mac OSX 中,使用 LF 來存儲新行。

在舊的 Mac 系統中,使用 CR 來存儲新行。

XML 以 LF 存儲換行。

XML 元素


XML 文檔包含 XML 元素。


什么是 XML 元素?

XML 元素指的是從(且包括)開始標簽直到(且包括)結束標簽的部分。

一個元素可以包含:

  • 其他元素
  • 文本
  • 屬性
  • 或混合以上所有...
<bookstore> <book category="CHILDREN"> <title>Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> <book category="WEB"> <title>Learning XML</title> <author>Erik T. Ray</author> <year>2003</year> <price>39.95</price> </book> </bookstore>

在上面的實例中,<bookstore> 和 <book> 都有 元素內容,因為他們包含其他元素。<book> 元素也有屬性(category="CHILDREN")。<title>、<author>、<year> 和 <price> 有文本內容,因為他們包含文本。


XML 命名規則

XML 元素必須遵循以下命名規則:

  • 名稱可以包含字母、數字以及其他的字符
  • 名稱不能以數字或者標點符號開始
  • 名稱不能以字母 xml(或者 XML、Xml 等等)開始
  • 名稱不能包含空格

可使用任何名稱,沒有保留的字詞。


最佳命名習慣

使名稱具有描述性。使用下划線的名稱也很不錯:<first_name>、<last_name>。

名稱應簡短和簡單,比如:<book_title>,而不是:<the_title_of_the_book>。

避免 "-" 字符。如果您按照這樣的方式進行命名:"first-name",一些軟件會認為您想要從 first 里邊減去 name。

避免 "." 字符。如果您按照這樣的方式進行命名:"first.name",一些軟件會認為 "name" 是對象 "first" 的屬性。

避免 ":" 字符。冒號會被轉換為命名空間來使用(稍后介紹)。

XML 文檔經常有一個對應的數據庫,其中的字段會對應 XML 文檔中的元素。有一個實用的經驗,即使用數據庫的命名規則來命名 XML 文檔中的元素。

在 XML 中,éòá 等非英語字母是完全合法的,不過需要留意,您的軟件供應商不支持這些字符時可能出現的問題。


XML 元素是可擴展的

XML 元素是可擴展,以攜帶更多的信息。

請看下面的 XML 實例:

<note> <to>Tove</to> <from>Jani</from> <body>Don't forget me this weekend!</body> </note>

讓我們設想一下,我們創建了一個應用程序,可將 <to>、<from> 以及 <body> 元素從 XML 文檔中提取出來,並產生以下的輸出:

MESSAGE

To: Tove
From: Jani

Don't forget me this weekend!

想象一下,XML 文檔的作者添加的一些額外信息:

<note> <date>2008-01-10</date> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note>

那么這個應用程序會中斷或崩潰嗎?

不會。這個應用程序仍然可以找到 XML 文檔中的 <to>、<from> 以及 <body> 元素,並產生同樣的輸出。

XML 的優勢之一,就是可以在不中斷應用程序的情況下進行擴展。

XML 屬性


XML元素具有屬性,類似 HTML。

屬性(Attribute)提供有關元素的額外信息。


XML 屬性

在 HTML 中,屬性提供有關元素的額外信息:

<img src="computer.gif">
<a href="demo.html">

屬性通常提供不屬於數據組成部分的信息。在下面的實例中,文件類型與數據無關,但是對需要處理這個元素的軟件來說卻很重要:

<file type="gif">computer.gif</file>

 


XML 屬性必須加引號

屬性值必須被引號包圍,不過單引號和雙引號均可使用。比如一個人的性別,person 元素可以這樣寫:

<person sex="female">

或者這樣也可以:

<person sex='female'>

如果屬性值本身包含雙引號,您可以使用單引號,就像這個實例:

<gangster name='George "Shotgun" Ziegler'>

或者您可以使用字符實體:

<gangster name="George &quot;Shotgun&quot; Ziegler">

 


XML 元素 vs. 屬性

請看這些實例:

<person sex="female">
<firstname>Anna</firstname>
<lastname>Smith</lastname>
</person>

 

<person>
<sex>female</sex>
<firstname>Anna</firstname>
<lastname>Smith</lastname>
</person>

在第一個實例中,sex 是一個屬性。在第二個實例中,sex 是一個元素。這兩個實例都提供相同的信息。

沒有什么規矩可以告訴我們什么時候該使用屬性,而什么時候該使用元素。我的經驗是在 HTML 中,屬性用起來很便利,但是在 XML 中,您應該盡量避免使用屬性。如果信息感覺起來很像數據,那么請使用元素吧。


我最喜歡的方式

下面的三個 XML 文檔包含完全相同的信息:

第一個實例中使用了 date 屬性:

<note date="10/01/2008">
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

第二個實例中使用了 date 元素:

<note>
<date>10/01/2008</date>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

第三個實例中使用了擴展的 date 元素(這是我的最愛):

<note>
<date>
<day>10</day>
<month>01</month>
<year>2008</year>
</date>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

 


避免 XML 屬性?

因使用屬性而引起的一些問題:

  • 屬性不能包含多個值(元素可以)
  • 屬性不能包含樹結構(元素可以)
  • 屬性不容易擴展(為未來的變化)

屬性難以閱讀和維護。請盡量使用元素來描述數據。而僅僅使用屬性來提供與數據無關的信息。

不要做這樣的蠢事(這不是 XML 應該被使用的方式):

<note day="10" month="01" year="2008"
to="Tove" from="Jani" heading="Reminder"
body="Don't forget me this weekend!">
</note>

 


針對元數據的 XML 屬性

有時候會向元素分配 ID 引用。這些 ID 索引可用於標識 XML 元素,它起作用的方式與 HTML 中 id 屬性是一樣的。這個實例向我們演示了這種情況:

<messages>
<note id="501">
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
<note id="502">
<to>Jani</to>
<from>Tove</from>
<heading>Re: Reminder</heading>
<body>I will not</body>
</note>
</messages>

上面的 id 屬性僅僅是一個標識符,用於標識不同的便簽。它並不是便簽數據的組成部分。

在此我們極力向您傳遞的理念是:元數據(有關數據的數據)應當存儲為屬性,而數據本身應當存儲為元素。

 

XML 驗證


擁有正確語法的 XML 被稱為"形式良好"的 XML。

通過 DTD 驗證的XML是"合法"的 XML。


形式良好的 XML 文檔

"形式良好"的 XML 文檔擁有正確的語法。

在前面的章節描述的語法規則:

  • XML 文檔必須有一個根元素
  • XML元素都必須有一個關閉標簽
  • XML 標簽對大小寫敏感
  • XML 元素必須被正確的嵌套
  • XML 屬性值必須加引號
<?xml version="1.0" encoding="ISO-8859-1"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

 


驗證 XML 文檔

合法的 XML 文檔是"形式良好"的 XML 文檔,這也符合文檔類型定義(DTD)的規則:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE note SYSTEM "Note.dtd">
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

在上面的實例中,DOCTYPE 聲明是對外部 DTD 文件的引用。下面的段落展示了這個文件的內容。


XML DTD

DTD 的目的是定義 XML 文檔的結構。它使用一系列合法的元素來定義文檔結構:

<!DOCTYPE note
[
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>

如果您想要學習 DTD,請在我們的首頁查找 DTD 教程。


XML Schema

W3C 支持一種基於 XML 的 DTD 代替者,它名為 XML Schema:

<xs:element name="note">

<xs:complexType>
<xs:sequence>
<xs:element name="to" type="xs:string"/>
<xs:element name="from" type="xs:string"/>
<xs:element name="heading" type="xs:string"/>
<xs:element name="body" type="xs:string"/>
</xs:sequence>
</xs:complexType>

</xs:element>

 

 

http://www.runoob.com/xml/xml-tutorial.html

 

使用xml.dom解析xml

文件對象模型(Document Object Model,簡稱DOM),是W3C組織推薦的處理可擴展置標語言的標准編程接口。

一個 DOM 的解析器在解析一個 XML 文檔時,一次性讀取整個文檔,把文檔中所有元素保存在內存中的一個樹結構里,之后你可以利用DOM 提供的不同的函數來讀取或修改文檔的內容和結構,也可以把修改過的內容寫入xml文件。

python中用xml.dom.minidom來解析xml文件,實例如下:

#!/usr/bin/python # -*- coding: UTF-8 -*- from xml.dom.minidom import parse import xml.dom.minidom # 使用minidom解析器打開 XML 文檔 DOMTree = xml.dom.minidom.parse("movies.xml") collection = DOMTree.documentElement if collection.hasAttribute("shelf"): print "Root element : %s" % collection.getAttribute("shelf") # 在集合中獲取所有電影 movies = collection.getElementsByTagName("movie") # 打印每部電影的詳細信息 for movie in movies: print "*****Movie*****" if movie.hasAttribute("title"): print "Title: %s" % movie.getAttribute("title") type = movie.getElementsByTagName('type')[0] print "Type: %s" % type.childNodes[0].data format = movie.getElementsByTagName('format')[0] print "Format: %s" % format.childNodes[0].data rating = movie.getElementsByTagName('rating')[0] print "Rating: %s" % rating.childNodes[0].data description = movie.getElementsByTagName('description')[0] print "Description: %s" % description.childNodes[0].data

以上程序執行結果如下:

Root element : New Arrivals *****Movie***** Title: Enemy Behind Type: War, Thriller Format: DVD Rating: PG Description: Talk about a US-Japan war *****Movie***** Title: Transformers Type: Anime, Science Fiction Format: DVD Rating: R Description: A schientific fiction *****Movie***** Title: Trigun Type: Anime, Action Format: DVD Rating: PG Description: Vash the Stampede! *****Movie***** Title: Ishtar Type: Comedy Format: VHS Rating: PG Description: Viewable boredom

用 ElementTree 在 Python 中解析 XML

原文: http://eli.thegreenplace.net/2012/03/15/processing-xml-in-python-with-elementtree/

譯者: TheLover_Z

當你需要解析和處理 XML 的時候,Python 表現出了它 “batteries included” 的一面。 標准庫 中大量可用的模塊和工具足以應對 Python 或者是 XML 的新手。

幾個月前在 Python 核心開發者之間發生了一場 有趣的討論 ,他們討論了 Python 下可用的 XML 處理工具的優點,還有如何將它們最好的展示給用戶看。這篇文章是我本人的拙作,我打算講講哪些工具比較好用還有為什么它們好用,當然,這篇文章也可以當作一個如何使用的基礎教程來看。

這篇文章所使用的代碼基於 Python 2.7,你稍微改動一下就可以在 Python 3.x 上面使用了。

應該使用哪個 XML 庫?

Python 有非常非常多的工具來處理 XML。在這個部分我想對 Python 所提供的包進行一個簡單的瀏覽,並且解釋為什么 ElementTree 是你最應該用的那一個。

xml.dom.* 模塊 - 是 W3C DOM API 的實現。如果你有處理 DOM API 的需要,那么這個模塊適合你。注意:在 xml.dom 包里面有許多模塊,注意它們之間的不同。

xml.sax.* 模塊 - 是 SAX API 的實現。這個模塊犧牲了便捷性來換取速度和內存占用。SAX 是一個基於事件的 API,這就意味着它可以“在空中”(on the fly)處理龐大數量的的文檔,不用完全加載進內存(見注釋1)。

xml.parser.expat - 是一個直接的,低級一點的基於 C 的 expat 的語法分析器(見注釋2)。 expat 接口基於事件反饋,有點像 SAX 但又不太像,因為它的接口並不是完全規范於 expat 庫的。

最后,我們來看看 xml.etree.ElementTree (以下簡稱 ET)。它提供了輕量級的 Python 式的 API ,它由一個 C 實現來提供。相對於 DOM 來說,ET 快了很多(見注釋3)而且有很多令人愉悅的 API 可以使用。相對於 SAX 來說,ET 也有 ET.iterparse 提供了 “在空中” 的處理方式,沒有必要加載整個文檔到內存。ET 的性能的平均值和 SAX 差不多,但是 API 的效率更高一點而且使用起來很方便。我一會兒會給你們看演示。

我的建議 是盡可能的使用 ET 來處理 XML ,除非你有什么非常特別的需要。

ElementTree - 一個 API ,兩種實現

ElementTree 生來就是為了處理 XML ,它在 Python 標准庫中有兩種實現。一種是純 Python 實現例如 xml.etree.ElementTree ,另外一種是速度快一點的 xml.etree.cElementTree 。你要記住: 盡量使用 C 語言實現的那種,因為它速度更快,而且消耗的內存更少。如果你的電腦上沒有 _elementtree (見注釋4) 那么你需要這樣做:

try: import xml.etree.cElementTree as ET except ImportError: import xml.etree.ElementTree as ET 

這是一個讓 Python 不同的庫使用相同 API 的一個比較常用的辦法。還是那句話,你的編譯環境和別人的很可能不一樣,所以這樣做可以防止一些莫名其妙的小問題。注意:從 Python 3.3 開始,你沒有必要這么做了,因為 ElementTree 模塊會自動尋找可用的 C 庫來加快速度。所以只需要 import xml.etree.ElementTree 就可以了。但是在 3.3 正式推出之前,你最好還是使用我上面提供的那段代碼。

將 XML 解析為樹的形式

我們來講點基礎的。XML 是一種分級的數據形式,所以最自然的表示方法是將它表示為一棵樹。ET 有兩個對象來實現這個目的 - ElementTree 將整個 XML 解析為一棵樹, Element 將單個結點解析為樹。如果是整個文檔級別的操作(比如說讀,寫,找到一些有趣的元素)通常用 ElementTree 。單個 XML 元素和它的子元素通常用 Element 。下面的例子能說明我剛才啰嗦的一大堆。(見注釋5)

我們用這個 XML 文件來做例子:

<?xml version="1.0"?>
<doc>
    <branch name="testing" hash="1cdf045c">
        text,source
    </branch>
    <branch name="release01" hash="f200013e">
        <sub-branch name="subrelease01">
            xml,sgml
        </sub-branch>
    </branch>
    <branch name="invalid">
    </branch>
</doc>

讓我們加載並且解析這個 XML :

>>> import xml.etree.cElementTree as ET >>> tree = ET.ElementTree(file='doc1.xml') 

然后抓根結點元素:

>>> tree.getroot() <Element 'doc' at 0x11eb780> 

和預期一樣,root 是一個 Element 元素。我們可以來看看:

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

看吧,根元素沒有任何狀態(見注釋6)。就像任何 Element 一樣,它可以找到自己的子結點:

>>> for child_of_root in root: ... print child_of_root.tag, child_of_root.attrib ... branch {'hash': '1cdf045c', 'name': 'testing'} branch {'hash': 'f200013e', 'name': 'release01'} branch {'name': 'invalid'} 

我們也可以進入一個指定的子結點:

>>> root[0].tag, root[0].text ('branch', '\n text,source\n ') 

找到我們感興趣的元素

從上面的例子我們可以輕而易舉的看到,我們可以用一個簡單的遞歸獲取 XML 中的任何元素。然而,因為這個操作比較普遍,ET 提供了一些有用的工具來簡化操作.

Element 對象有一個 iter 方法可以對子結點進行深度優先遍歷。 ElementTree 對象也有 iter 方法來提供便利。

>>> for elem in tree.iter(): ... print elem.tag, elem.attrib ... doc {} branch {'hash': '1cdf045c', 'name': 'testing'} branch {'hash': 'f200013e', 'name': 'release01'} sub-branch {'name': 'subrelease01'} branch {'name': 'invalid'} 

遍歷所有的元素,然后檢驗有沒有你想要的。ET 可以讓這個過程更便捷。 iter 方法接受一個標簽名字,然后只遍歷那些有指定標簽的元素:

>>> for elem in tree.iter(tag='branch'): ... print elem.tag, elem.attrib ... branch {'hash': '1cdf045c', 'name': 'testing'} branch {'hash': 'f200013e', 'name': 'release01'} branch {'name': 'invalid'} 

來自 XPath 的幫助

為了尋找我們感興趣的元素,一個更加有效的辦法是使用 XPath 支持。 Element 有一些關於尋找的方法可以接受 XPath 作為參數。 find 返回第一個匹配的子元素, findall 以列表的形式返回所有匹配的子元素, iterfind 為所有匹配項提供迭代器。這些方法在 ElementTree 里面也有。

給出一個例子:

>>> for elem in tree.iterfind('branch/sub-branch'): ... print elem.tag, elem.attrib ... sub-branch {'name': 'subrelease01'} 

這個例子在 branch 下面找到所有標簽為 sub-branch 的元素。然后給出如何找到所有的 branch 元素,用一個指定 name 的狀態即可:

>>> for elem in tree.iterfind('branch[@name="release01"]'): ... print elem.tag, elem.attrib ... branch {'hash': 'f200013e', 'name': 'release01'} 

想要深入學習 XPath 的話,請看 這里 。

建立 XML 文檔

ET 提供了建立 XML 文檔和寫入文件的便捷方式。 ElementTree 對象提供了 write 方法。

現在,這兒有兩個常用的寫 XML 文檔的腳本。

修改文檔可以使用 Element 對象的方法:

>>> root = tree.getroot() >>> del root[2] >>> root[0].set('foo', 'bar') >>> for subelem in root: ... print subelem.tag, subelem.attrib ... branch {'foo': 'bar', 'hash': '1cdf045c', 'name': 'testing'} branch {'hash': 'f200013e', 'name': 'release01'} 

我們在這里刪除了根元素的第三個子結點,然后為第一個子結點增加新狀態。然后這個樹可以寫回到文件中。

>>> import sys >>> tree.write(sys.stdout) # ET.dump can also serve this purpose <doc>  <branch foo="bar" hash="1cdf045c" name="testing">  text,source  </branch> <branch hash="f200013e" name="release01">  <sub-branch name="subrelease01">  xml,sgml  </sub-branch> </branch> </doc> 

注意狀態的順序和原文檔的順序不太一樣。這是因為 ET 講狀態保存在無序的字典中。語義上來說,XML 並不關心順序。

建立一個全新的元素也很容易。ET 模塊提供了 SubElement 函數來簡化過程:

>>> a = ET.Element('elem') >>> c = ET.SubElement(a, 'child1') >>> c.text = "some text" >>> d = ET.SubElement(a, 'child2') >>> b = ET.Element('elem_b') >>> root = ET.Element('root') >>> root.extend((a, b)) >>> tree = ET.ElementTree(root) >>> tree.write(sys.stdout) <root><elem><child1>some text</child1><child2 /></elem><elem_b /></root> 

使用 iterparse 來處理 XML 流

就像我在文章一開頭提到的那樣,XML 文檔通常比較大,所以將它們全部讀入內存的庫可能會有點兒小問題。這也是為什么我建議使用 SAX API 來替代 DOM 。

我們剛講過如何使用 ET 來將 XML 讀入內存並且處理。但它就不會碰到和 DOM 一樣的內存問題么?當然會。這也是為什么這個包提供一個特殊的工具,用來處理大型文檔,並且解決了內存問題,這個工具叫 iterparse 。

我給大家演示一個 iterparse 如何使用的例子。我用 自動生成 拿到了一個 XML 文檔來進行說明。這只是開頭的一小部分:

<?xml version="1.0" standalone="yes"?>
<site>
    <regions>
        <africa>
            <item id="item0">
                <location>United States</location>    <!-- Counting locations -->
                <quantity>1</quantity>
                <name>duteous nine eighteen </name>
                <payment>Creditcard</payment>
                <description>
                    <parlist>
[...]

我已經用注釋標出了我要處理的元素,我們用一個簡單的腳本來計數有多少 location 元素並且文本內容為“Zimbabwe”。這是用 ET.parse 的一個標准的寫法:

tree = ET.parse(sys.argv[2]) count = 0 for elem in tree.iter(tag='location'): if elem.text == 'Zimbabwe': count += 1 print count 

所有 XML 樹中的元素都會被檢驗。當處理一個大約 100MB 的 XML 文件時,占用的內存大約是 560MB ,耗時 2.9 秒。

注意:我們並不需要在內存中加載整顆樹。它檢測我們需要的帶特定值的 location 元素。其他元素被丟棄。這是 iterparse 的來源:

count = 0 for event, elem in ET.iterparse(sys.argv[2]): if event == 'end': if elem.tag == 'location' and elem.text == 'Zimbabwe': count += 1 elem.clear() # discard the element print count 

這個循環遍歷 iterparse 事件,檢測“閉合的”(end)事件並且尋找 location 標簽和指定的值。在這里 elem.clear() 是關鍵 - iterparse 仍然建立一棵樹,只不過不需要全部加載進內存,這樣做可以有效的利用內存空間(見注釋7)。

處理同樣的文件,這個腳本占用內存只需要僅僅的 7MB ,耗時 2.5 秒。速度的提升歸功於生成樹的時候只遍歷一次。相比較來說, parse 方法首先建立了整個樹,然后再次遍歷來尋找我們需要的元素(所以慢了一點)。

結論

在 Python 眾多處理 XML 的模塊中, ElementTree 真是屌爆了。它將輕量,符合 Python 哲學的 API ,出色的性能完美的結合在了一起。所以說如果要處理 XML ,果斷地使用它吧!

這篇文章簡略地談了談 ET 。我希望這篇拙作可以拋磚引玉。

注釋

注釋1:和 DOM 不一樣,DOM 將整個 XML 加載進內存並且允許隨機訪問任何深度地元素。

注釋2: expat 是一個開源的用於處理 XML 的 C 語言庫。Python 將它融合進自身。

注釋3:Fredrik Lundh,是 ElementTree 的原作者,他提到了一些 基准 。

注釋4:當我提到 _elementtree 的時候,我意思是 C 語言的 cElementTree._elementtree 擴展模塊。

注釋5:確定你手邊有 模塊手冊 然后可以隨時查閱我提到的方法和函數。

注釋6: 狀態 是一個意義太多的術語。Python 對象有狀態,XML 元素也有狀態。希望我能將它們表達的更清楚一點。

注釋7:准確來說,樹的根元素仍然存活。在某些情況下根結點非常大,你也可以丟棄它,但那需要多一點點代碼。

Python基於DOM的XML編程接口

Python自帶支持XML DOM的模塊:xml.dom和xml.dom.minidom。

 
模塊包含的函數
getDOMImplementation( )   
返回一個DOMImplementation對象,由該對象的createDocument( )方法創建一個DOM對象。
代碼示例:
    from xml.dom.minidom import getDOMImplementation
    impl = getDOMImplementation()
    newdoc = impl.createDocument(None, "some_tag", None)
    top_element = newdoc.documentElement
    text = newdoc.createTextNode('Some textual content.')
    top_element.a(text)
 
DOMImplementation對象
DOMImplementation對象中的createDocument()方法創建並返回一個Document對象,語法:
createDocument( namespaceUriqualifiedNamedoctype)
前兩個命令參數用於指定命名空間、頂級標簽,可以都是None, 表示沒有任何文檔元素。參數doctype必須是由createDocumentType()方法創建的DocumentType對象,或是None。
 
DOMImplementation對象中的createDocumentType()方法創建並返回一個DocumentType對象,語法:
createDocumentType( qualifiedNamepublicIdsystemId)
返回的DocumentType對象由參數qualifiedName, publicId和systemId字串封裝。
 
從外部文件創建DOM對象
代碼示例:
    from xml.dom.minidom import parse, parseString
    dom1 = parse('c:\\temp\\mydata.xml')      # 直接指定XML文件名創建DOM對象。
    datasource = open('c:\\temp\\mydata.xml')   
    dom2 = parse(datasource)                          # 由一個已打開的文件句柄創建DOM對象。
    dom3 = parseString('< myxml > ... < /myxml >')     #從XML描述字符串創建DOM對象。
    assert dom3.documentElement.tagName == "myxml"
 
minidom中Node對象特有的方法
Python的DOM API說明主要包含在“xml.dom”模塊文檔中, “xml.dom.minidom”有如下一些特有的方法:
(1)Node.unlink( )
斷開對象的所有引用,通過Python垃圾回收機制,對象所占用的內存空間能夠被盡快釋放。該方法主要用在Document對象,但也可以用於其下級子對象,釋放某個子對象的內存空間。
(2)Node.toxml([ encoding])
返回XML DOM對象的可顯示字符串。如果參數缺省,XML頭中將不指定字符集,此時默認字符集如不能顯示所有字符,將選用Unicode字符集。"utf-8"是XML的缺省字符集。
(3)Node.toprettyxml([ indent=""[,  newl=""[,  encoding=""]]])
返回進行過優化處理的可顯示字符串。參數缺省情況下:indent為制表符(tabulator);newl為新行符(\n)。
 
Node對象
XML文檔的所有組件都可以看作是Node對象。
Node.nodeType    用一個整數表示Node對象的類型。該屬性為只讀。
Node.parentNode    當前對象的雙親對象,對於Document對象該屬性為None,對於Attr對象總是None。 該屬性只讀。
Node.attributes    包含的屬性對象,只有節點對象的類型為元素是才有實際的值。該屬性只讀。
Node.previousSibling    與當前節點同屬一個雙親節點的前一個緊鄰的節點。對於第一個節點,該屬性為
None。 該屬性只讀。
Node.nextSibling    與當前節點同屬一個雙親節點的后一個緊鄰的節點。對於最后一個節點,該屬性為None。 該屬性只讀。
Node.childNodes    一個包含當前節點的子節點的列表。該屬性只讀。
Node.firstChild    第一個子節點,可以是None。 該屬性只讀。
Node.lastChild    最后一個子節點,可以是None。 該屬性只讀。
Node.localName    本地標簽名稱,完整名稱中如有冒號,則為冒號之后的部分,否則為完整名稱。該屬性為字符串。
Node.prefix    名稱前綴,完整名稱中如有冒號,則為冒號之前的部分,否則為空。該屬性為字符串,或None。
Node.namespaceURI    與元素關聯的命名空間,為一字符串或None。該屬性只讀。
Node.nodeName    對於元素節點,等同於tagName;對於屬性節點,等同於name。該節點只讀。
Node.nodeValue    與nodeName類似,對於不同類型的節點,有不同的含義。該屬性只讀。
Node.hasAttributes()    如果節點包含有屬性,返回True。
Node.hasChildNodes()    如果節點包含子節點,返回True。
Node.isSameNode(other)    如果指定的節點對象引用的就是本節點,該方法返回True。 
Node.append Child( newChild)    在當前節點的最后一個子節點后增加一個新的子節點。該方法將返回新子節點。 
Node.insertBefore( newChildrefChild)    在指定的子節點前插入一個新的節點,如果參照的節點為None,新節點將插入到子節點列表的末尾。該方法返回新子節點。 
Node.removeChild( oldChild)    刪除一個子節點,該方法執行成功后返回被刪除的節點,應適用unlink()方法釋放被刪除節點占用的內存。  
Node.replaceChild( newChildoldChild)    用一個新的節點替換一個已經存在的子節點。
Node.normalize()    將鄰接的文本節點(textNode)連接為單個文本實例,以簡化對文本的處理。
Node.cloneNode( deep)    克隆當前節點,設置deep表示克隆所有的子節點。該方法返回克隆節點。 
 
NodeList對象
NodeList是一個有序的對象列表。它通常用來表示一個節點的子節點列表,或執行getElementsByTagName_r()和getElementsByTagNameNS()方法返回的對象列表。
NodeList.item( i)    返回序列中的第i各元素,如果不存在返回None。
NodeList.length    序列中節點的數量。
 
DocumentType對象
Document對象的doctype屬性是一個DocumentType對象。 它是一類特殊的節點,有如下屬性:
DocumentType.publicId    文檔類型定義外部子集中的公共標識符,可以是一個字符串,或None。
DocumentType.systemId    文檔類型定義外部子集中的系統標識符,可以是一個URI字符串,或None。
DocumentType.internalSubset
DocumentType.name    如果存在,用來聲明根元素的名稱。
DocumentType.entities
DocumentType.notations
 
 Document對象
Document代表整個XML文檔,由getDOMImplementation()創建,它從Node繼承屬性,nodeType值為9。
 
Document.documentElement    Document對象的根元素。
 
document.  create Element( tagName)    建立並返回一個新元素節點,但要加入的節點樹中,需使用insertBefore() 或 append Child()方法。
document.  create ElementNS( namespaceURItagName)    
 
document.  create TextNode( data)    以參數傳入的數據建立並返回一個文本節點,但未加入到節點樹中。 
 
document.  create Comment( data)    以參數傳入的數據建立並返回一個注釋節點,但未加入到節點樹中。
 
document.  create ProcessingInstruction( targetdata)    
 
document.  create Attribute( name)    建立並返回一個屬性節點,但要關聯到一個元素節點,需要使用方法setAttributeNode()。
document.  create AttributeNS( namespaceURIqualifiedName)   
 
Document.getElementsByTagName  ( tagName)    在所有后代節點中查找指定tagname的元素節點,返回子元素到列表,注意是列表。
Document.getElementsByTagNameNS( namespaceURIlocalName)    
 
Element對象
Element是Node的子類,nodeType值為1。
Element.tagName    元素類型名稱。 
Element.getElementsByTagName  ( tagName)    與Document類相同。
Element.getElementsByTagNameNS( namespaceURIlocalName)
Element.hasAttribute( name)  
Element.hasAttributeNS( namespaceURIlocalName)
Element.getAttribute( name)    
Element.getAttributeNode( attrname)    返回屬性節點。
Element.getAttributeNS( namespaceURIlocalName)
Element.getAttributeNodeNS( namespaceURIlocalName)
Element.removeAttribute( name)
Element.removeAttributeNode( oldAttr)
Element.removeAttributeNS( namespaceURIlocalName)
Element.setAttribute( namevalue)
Element.setAttributeNode( newAttr)
Element.setAttributeNodeNS( newAttr)
Element.setAttributeNS( namespaceURIqnamevalue)
 
Attr對象
Attr繼承自Node。 nodeType值為2。
Attr.name    屬性名稱,存在命名空間時,名稱中包含冒號。
Attr.localName   存在命名空間時,為冒號后的部分。
Attr.prefix    存在命名空間時,為冒號前的部分。
Attr.value    文本類型的屬性值,與nodeValue同義。
 
NamedNodeMap對象
NamedNodeMap繼承自Node。
NamedNodeMap.length    屬性列表的長度。
NamedNodeMap.item( index)    返回指定索引的屬性項,通過value屬性獲得屬性值。
 
Comment對象
表示XML文檔中的注釋,是Node的子類,本身不能有自節點。
Comment.data    注釋文本字符串。
 
Text和CDATASection對象
Text接口表示XML文檔中的文本,它不存在子節點。如果支持XML擴展,CDATA標記中的文本將被保存在CDATASection對象中。Text節點到nodeType值為3。
Text.data    節點的文本字符串。
 
ProcessingInstruction對象
表示XML文檔中的處理指定,它不存在子節點。
ProcessingInstruction.target    處理指令到出現的第一個空格為止的內容,是只讀屬性。
ProcessingInstruction.data    處理指令出現的第一個空格字符之后的內容。
 
異常類
DOM級別2 推薦定義單一的異常類DOMException作為基類,並通過定義一系列的常量為可能發生的錯誤分類。DOMException實例的code屬性用來存儲表示錯誤類型的值。異常類型常量對應的派生異常類如下:
    DOMSTRING_SIZE_ERR DomstringSizeErr
    HIERARCHY_REQUEST_ERR HierarchyRequestErr
    INDEX_SIZE_ERR IndexSizeErr
    INUSE_ATTRIBUTE_ERR InuseAttributeErr
    INVALID_ACCESS_ERR InvalidAccessErr
    INVALID_CHARACTER_ERR InvalidCharacterErr
    INVALID_MODIFICATION_ERR InvalidModificationErr
    INVALID_STATE_ERR InvalidStateErr
    NAMESPACE_ERR NamespaceErr
    NOT_FOUND_ERR NotFoundErr
    NOT_SUPPORTED_ERR NotSupportedErr
    NO_DATA_ALLOWED_ERR NoDataAllowedErr
    NO_MODIFICATION_ALLOWED_ERR NoModificationAllowedErr
    SYNTAX_ERR SyntaxErr
    WRONG_DOCUMENT_ERR WrongDocumentErr

 http://blog.sina.com.cn/s/blog_da4487c40102v3jx.html

使用minidom來處理XML的示例(Python 學習)(轉載)

一.XML的讀取.

在 NewEdit 中有代碼片段的功能,代碼片段分為片段的分類和片段的內容。在缺省情況下都是用XML格式保存的。下面我講述一下,如何使用minidom來讀取和保存XML文件。

下面是片段分類的一個示例文件--catalog.xml

<?xml version="1.0" encoding="utf-8"?>
<catalog>
    <maxid>4</maxid>
    <item id="1">
        <caption>Python</caption>
        <item id="4">
            <caption>測試</caption>
        </item>
    </item>
    <item id="2">
        <caption>Zope</caption>
    </item>
</catalog>

分類是樹狀結構,顯示出來可能為:

Python
    測試
Zope

先簡單介紹一下XML的知識,如果你已經知道了可以跳過去。

1. XML文檔的編碼

此XML文檔的編碼為utf-8,因此你看到的“測試”其實是UTF-8編碼。在XML文檔的處理中都是使用UTF-8編碼進行的,因此,如果你不寫明encoding的話,都是認為文件是UTF-8編碼的。在Python中,好象只支持幾種編碼,象我們常用的GB2312碼就不支持,因此建議大家在處理XML時使用UTF-8編碼。

2. XML文檔的結構

XML文檔有XML頭信息和XML信息體。頭信息如:

<?xml version="1.0" encoding="utf-8"?>

它表明了此XML文檔所用的版本,編碼方式。有些復雜的還有一些文檔類型的定義(DOCTYPE),用於定義此XML文檔所用的DTD或Schema和一些實體的定義。這里並沒有用到,而且我也不是專家,就不再細說了。

XML信息體是由樹狀元素組成。每個XML文檔都有一個文檔元素,也就是樹的根元素,所有其它的元素和內容都包含在根元素中。

3. DOM

DOM是Document Object Model的簡稱,它是以對象樹來表示一個XML文檔的方法,使用它的好處就是你可以非常靈活的在對象中進行遍歷。

4. 元素和結點

元素就是標記,它是成對出現的。XML文檔就是由元素組成的,但元素與元素之間可以有文本,元素的內容也是文本。在minidom中有許多的結點,元素也屬於結點的一種,它不是葉子結點,即它存在子結點;還存在一些葉子結點,如文本結點,它下面不再有子結點。

象catalog.xml中,文檔元素是catalog,它下面有兩種元素:maxid和item。maxid用來表示當前最大的item的id值。每一個item都有一個id屬性,id屬性是唯一的,在 NewEdit 中用來生成每個分類所對應的代碼片段的XML文檔名,因此不能重復,而且它是一個遞增的值。item元素有一個caption子元素,用來表示此分類項的名稱,它還可以包含item元素。這樣,就定義了一個樹狀XML結構,下面讓我們看一看如果把它們讀出來。


一、得到dom對象

>>> import xml.dom.minidom
>>> dom = xml.dom.minidom.parse('d:/catalog.xml')

這樣我們得到了一個dom對象,它的第一個元素應該是catalog。

二、得到文檔元素對象

>>> root = dom.documentElement

這樣我們得到了根元素(catalog)。

三、結點屬性

每一個結點都有它的nodeName,nodeValue,nodeType屬性。nodeName為結點名字。

>>> root.nodeName
u'catalog'

nodeValue是結點的值,只對文本結點有效。nodeType是結點的類型,現在有以下幾種:

'ATTRIBUTE_NODE'
'CDATA_SECTION_NODE'
'COMMENT_NODE'
'DOCUMENT_FRAGMENT_NODE'
'DOCUMENT_NODE'
'DOCUMENT_TYPE_NODE'
'ELEMENT_NODE'
'ENTITY_NODE'
'ENTITY_REFERENCE_NODE'
'NOTATION_NODE'
'PROCESSING_INSTRUCTION_NODE'
'TEXT_NODE'

這些結點通過名字很好理解。catalog是ELEMENT_NODE類型。

>>> root.nodeType
1
>>> root.ELEMENT_NODE
1

四、子元素、子結點的訪問

訪問子元素、子結點的方法很多,對於知道元素名字的子元素,可以使用getElementsByTagName方法,如讀取maxid子元素:

>>> root.getElementsByTagName('maxid')
[<DOM Element: maxid at 0xb6d0a8>]

這樣返回一個列表,由於我們的例子中maxid只有一項,因此列表也只有一項。

如果想得到某個元素下的所有子結點(包括元素),可以使用childNodes屬性:

>>> root.childNodes
[<DOM Text node "\n    ">, <DOM Element: maxid at 0xb6d0a8>, <DOM Text node "\n    ">, <DOM Element: item at 0xb6d918>, <DOM Text node "\n    ">, <DOM Element: item at 0xb6de40>, <DOM Text node "\n    ">, <DOM Element: item at 0xb6dfa8>, <DOM Text node "\n">]

可以看出所有兩個標記間的內容都被視為文本結點。象每行后面的回車,都被看到文本結點。從上面的結果我們可以看出每個結點的類型,本例中有文本結點和元素結點;結點的名字(元素結點);結點的值(文本結點)。每個結點都是一個對象,不同的結點對象有不同的屬性和方法,更詳細的要參見文檔。由於本例比較簡單,只涉及文本結點和元素結點。

getElementsByTagName可以搜索當前元素的所有子元素,包括所有層次的子元素。childNodes只保存了當前元素的第一層子結點。

這樣我們可以遍歷childNodes來訪問每一個結點,判斷它的nodeType來得到不同的內容。如,打印出所有元素的名字:

>>> for node in root.childNodes:
    if node.nodeType == node.ELEMENT_NODE:
        print node.nodeName
        
maxid
item
item

對於文本結點,想得到它的文本內容可以使用: .data屬性。

對於簡單的元素,如:<caption>Python</caption>,我們可以編寫這樣一個函數來得到它的內容(這里為Python)。

def getTagText(root, tag):
    node = root.getElementsByTagName(tag)[0]
    rc = ""
    for node in node.childNodes:
        if node.nodeType in ( node.TEXT_NODE, node.CDATA_SECTION_NODE):
            rc = rc + node.data
    return rc

這個函數只處理找到的第一個符合的子元素。它會將符合的第一個子元素中的所有文本結點拼在一起。當nodeType為文本類結點時,node.data為文本的內容。如果我們考查一下元素caption,我們可能看到:

[<DOM Text node "Python">]

說明caption元素只有一個文本結點。

如果一個元素有屬性,那么可以使用getAttribute方法,如:

>>> itemlist = root.getElementsByTagName('item')
>>> item = itemlist[0]
>>> item.getAttribute('id')
u'1'

這樣就得到了第一個item元素的屬性值。

下面讓我們簡單地小結一下如何使用minidom來讀取XML中的信息

1. 導入xml.dom.minidom模塊,生成dom對象
2. 得到文檔對象(根對象)
3. 通過getElementsByTagName()方法和childNodes屬性(還有其它一些方法和屬性)找到要處理的元素
4. 取得元素下文本結點的內容


二.寫入.

下面我來演示一下如何從無到有生成象catalog.xml一樣的XML文件。

一、生成dom對象

>>> import xml.dom.minidom
>>> impl = xml.dom.minidom.getDOMImplementation()
>>> dom = impl.createDocument(None, 'catalog', None)

這樣就生成了一個空的dom對象。其中catalog為文檔元素名,即根元素名。

二、顯示生成的XML內容

每一個dom結點對象(包括dom對象本身)都有輸出XML內容的方法,如:toxml(), toprettyxml()

toxml()輸出緊湊格式的XML文本,如:

<catalog><item>test</item><item>test</item></catalog>

toprettyxml()輸出美化后的XML文本,如:

<catalog>
    <item>
        test
    </item>
    <item>
        test
    </item>
</catalog>

可以看出,它是將每個結點后面都加入了回車符,並且自動處理縮近。但對於每一個元素,如果元素只有文本內容,則我希望元素的tag與文本是在一起的,如:

<item>test</item>

而不想是分開的格式,但minidom本身是不支持這樣的處理。關於如何實現形如:

<catalog>
    <item>test</item>
    <item>test</item>
</catalog>

這樣的XML格式,后面我們再說。

三、生成各種結點對象

dom對象擁有各種生成結點的方法,下面列出文本結點,CDATA結點和元素結點的生成過程。

1. 文本結點的生成

>>> text=dom.createTextNode('test')
test

要注意的是,在生成結點時,minidom並不對文本字符進行檢查,象文本中如果出現了'<','&'之類的字符,應該轉換為相應的實體符號'&lt;','&amp;'才可以,這里沒有做這個處理。

2. CDATA結點的生成

>>> data = dom.createCDATASection('aaaaaa\nbbbbbb')
>>> data.toxml()
'<![CDATA[aaaaaa\nbbbbbb]]>'

CDATA是用於包括大塊文本,同時可以不用轉換'<','&'字符的標記,它是用<![CDATA[文本]]>來包括的。但文本中不可以有"]]>"這樣的串存在。生成結點時minidom不作這些檢查,只有當你輸出時才有可能發現有錯。

3. 元素結點的生成

>>> item = dom.createElement('caption')
>>> item.toxml()
'<caption/>'

對於象元素這樣的結點,生成的元素結點其實是一個空元素,即不包含任何文本,如果要包含文本或其它的元素,我們需要使用appendChild()或insertBefore()之類的方法將子結點加就到元素結點中。如將上面生成的text結點加入到caption元素結點中:

>>> item.appendChild(text)
<DOM Text node "test">
>>> item.toxml()
'<caption>test</caption>'

使用元素對象的setAttribute()方法可以向元素中加入屬性,如:

>>> item.setAttribute('id', 'idvalue')
>>> item.toxml()
'<caption id="idvalue">test</caption>'

四、生成dom對象樹

我們有了dom對象,又知道了如何生成各種結點,包括葉子結點(不包含其它結點的結點,如文本結點)和非葉子結點(包含其它結點的結點,如元素結點)的生成,然后就需要利用結點對象本身的appendChild()或insertBefore()方法將各個結點根據在樹中的位置連起來,串成一棵樹。最后要串到文檔結點上,即根結點上。如一個完整的示例為:

>>> import xml.dom.minidom
>>> impl = xml.dom.minidom.getDOMImplementation()
>>> dom = impl.createDocument(None, 'catalog', None)
>>> root = dom.documentElement
>>> item = dom.createElement('item')
>>> text = dom.createTextNode('test')
>>> item.appendChild(text)
<DOM Text node "test">
>>> root.appendChild(item)
<DOM Element: item at 0xb9cf80>
>>> print root.toxml()
<catalog><item>test</item></catalog>

五、簡單生成元素結點的函數

下面是我寫的一個小函數,用於簡單的生成類似於:

<caption>test</caption>

或形如:

<item><![CDATA[test]]></item>

的元素結點

1       def makeEasyTag(dom, tagname, value, type='text'):
2           tag = dom.createElement(tagname)
3           if value.find(']]>') > -1:
4               type = 'text'
5           if type == 'text':
6               value = value.replace('&', '&amp;')
7               value = value.replace('<', '&lt;')
8               text = dom.createTextNode(value)
9           elif type == 'cdata':
10              text = dom.createCDATASection(value)
11          tag.appendChild(text)
12          return tag

參數說明:

  • dom為dom對象
  • tagname為要生成元素的名字,如'item'
  • value為其文本內容,可以為多行
  • type為文本結點的格式,'text'為一般Text結點,'cdata'為CDATA結點

函數處理說明:

  • 首先創建元素結點
  • 查找文本內容是否有']]>',如果找到,則此文本結點只可以是Text結點
  • 如果結點類型為'text',則對文本內容中的'<'替換為'&lt;','&'替換為'&amp;',再生成文本結點
  • 如果結點類型為'cdata',則生成CDATA結點
  • 將生成的文本結點追加到元素結點上

因此這個小函數可以自動地處理字符轉化及避免CDATA結點中出現']]>'串。

上面生成'item'結點的語句可以改為:

>>> item = makeEasyTag(dom, 'item', 'test')
>>> item.toxml()
'<item>test</item>'

六、寫入到XML文件中

dom對象樹已經生成好了,我們可以調用dom的writexml()方法來將內容寫入文件中。writexml()方法語法格式為:

writexml(writer, indent, addindent, newl, encoding)

  • writer是文件對象
  • indent是每個tag前填充的字符,如:'  ',則表示每個tag前有兩個空格
  • addindent是每個子結點的縮近字符
  • newl是每個tag后填充的字符,如:'\n',則表示每個tag后面有一個回車
  • encoding是生成的XML信息頭中的encoding屬性值,在輸出時minidom並不真正進行編碼的處理,如果你保存的文本內容中有漢字,則需要自已進行編碼轉換。

writexml方法是除了writer參數必須要有外,其余可以省略。下面給出一個文本內容有漢字的示例:

1       >>> import xml.dom.minidom
2       >>> impl = xml.dom.minidom.getDOMImplementation()
3       >>> dom = impl.createDocument(None, 'catalog', None)
4       >>> root = dom.documentElement
5       >>> text = unicode('漢字示例', 'cp936')
6       >>> item = makeEasyTag(dom, 'item', text)
7       >>> root.appendChild(item)
8       <DOM Element: item at 0xb9ceb8>
9       >>> root.toxml()
10      u'<catalog><item>\u6c49\u5b57\u793a\u4f8b</item></catalog>'
11      >>> f=file('d:/test.xml', 'w')
12      >>> import codecs
13      >>> writer = codecs.lookup('utf-8')[3](f)
14      >>> dom.writexml(writer, encoding='utf-8')
15      >>> writer.close()

5行 因為XML處理時內部使用Unicode編碼,因此象漢字首先要轉成Unicode,如果你不做這一步minicode並不檢查,並且保存時可能不會出錯。但讀取時可能會出錯。
12-13行 生成UTF-8編碼的寫入流對象,這樣在保存時會自動將Unicode轉換成UTF-8編碼。

這樣寫XML文件就完成了。

三.美化.

對於dom對象的writexml()方法,雖然可以控制一些格式上的輸出,但結果並不讓人滿意。比如我想實現:

<catalog>
    <item>test</item>
    <item>test</item>
</catalog>

而不是:

<catalog>
    <item>
        test
    </item>
    <item>
        test
    </item>
</catalog>

如果是象下面的輸出結果我無法區分原來文本中是否帶有空白,而上一種結果則不存在這一問題。好在我在wxPython自帶的XML資源編輯器(xred)發現了美化的代碼。代碼如下:

1       def Indent(dom, node, indent = 0):
2           # Copy child list because it will change soon
3           children = node.childNodes[:]
4           # Main node doesn't need to be indented
5           if indent:
6               text = dom.createTextNode('\n' + '\t' * indent)
7               node.parentNode.insertBefore(text, node)
8           if children:
9               # Append newline after last child, except for text nodes
10              if children[-1].nodeType == node.ELEMENT_NODE:
11                  text = dom.createTextNode('\n' + '\t' * indent)
12                  node.appendChild(text)
13              # Indent children which are elements
14              for n in children:
15                  if n.nodeType == node.ELEMENT_NODE:
16                      Indent(dom, n, indent + 1)

參數說明:

dom為dom對象
node為要處理的元素結點
indent指明縮近的層數

函數說明:

Indent是一個遞歸函數,當一個結點有子元素時進行遞歸處理。主要是解決子元素的換行和縮近的處理。這里縮近是寫死的,每一級縮近使用一個制表符。如果你願意可以改為你想要的內容。就是把函數中的'\t'換替一下。或干脆寫成一個全局變量,或參數以后改起來可能要容易的多。不過在 NewEdit 中,這樣的處理足夠了,就沒有做這些工作。

Indent基本的想法就是遞歸遍歷所有子結點,在所有需要加入回車和縮近的地方插入相應的文本結點。這樣再使用writexml()輸出時就是縮近好了的。具體程序不再細說,直接用就行了。

但這里要注意的是:

Indent()要修改原dom對象,因此在調用它之前最好先復制一個臨時dom對象,使用完畢后再清除這個臨時dom對象即可。下面是詳細的調用過程:

1       domcopy = dom.cloneNode(True)
2       Indent(domcopy, domcopy.documentElement)
3       f = file(xmlfile, 'wb')
4       writer = codecs.lookup('utf-8')[3](f)
5       domcopy.writexml(writer, encoding = 'utf-8')
6       domcopy.unlink()

1行 克隆一個dom對象
2行 進行縮近處理
3-4行 進行UTF-8編碼處理
5行 生成XML文件
6行 清除dom對象的內容

Python 使用 ElementTree 處理 XML

一、引用方法

ElementTree 所在文件保存在 Lib/xml/etree/ElementTree.py,所以我們通過下面的代碼引用它,之后就可以使用 ET. 來訪問 ElementTree 中的函數。

1
import xml.etree.ElementTree as ET

 

二、一個 XML 例子

下面所有的操作都將下面這段 XML 為例,我們將它保存為sample.xml。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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: 使用 < 和 > 包圍的部分,如 成為 start-tag,是 end-tags;
  • Element:被 Tag 包圍的部分,如68,可以認為是一個節點,它可以有子節點;
  • Attribute:在 Tag 中可能存在的 name/value 對,如 中的 name=”Liechtenstein”,一般表示屬性。

三、解析 XML

讀入 XML 數據

首先讀入 XML,有兩種途徑,從文件讀入和從字符串讀入。
從文件讀入:

1
2
3
import xml.etree.ElementTree as ET
tree = ET.parse('sample.xml')
root = tree.getroot()

 

從字符串讀入:

1
root = ET.fromstring(sample_as_string)

 

tree 和 root 分布是 ElementTree 中兩個很重要的類的對象:

  • ElementTree
  • Element

查看 Tag 和 Attribute

這時得到的 root 是一個指向Element 對象,我們可以通過查看 root 的 tag 和 attrib 來驗證這一點:

1
2
3
4
>>> root.tag
'data'
>>> root.attrib
{}

 

上面的代碼說明了查看一個 Element 的 Tag 和 Attribute 的方法,Tag 是一個 字符串 ,而 Attribute 得到的是一個 字典

另外,還可以使用

  • Element.get(AttributeName)

來代替 Element.attrib[AttributeName]來訪問。

查看孩子

root.attrib 返回的是一個空字典,如果看 root 的孩子,可以得到非空的 attrib 字典。

1、使用 for…in…訪問

1
2
for child in root:
print child.tag, child.attrib

得到

country {‘name’: ‘Liechtenstein’}
country {‘name’: ‘Singapore’}
country {‘name’: ‘Panama’}

2、使用下標訪問

如:

1
2
3
4
>>> print root[0].tag
country
>>> print root[0][0].tag
rank

 

3、使用 Tag 名稱訪問

下標訪問的方法雖然簡單,但是在未知 XML 具體結構的時候並不適用,通過 Tag 名稱訪問的方法更具有普適性。這里用到 Element 類的幾個函數,分別是

  • Element.iter()
  • Element.findall()
  • Element.find()

這兩個函數使用的場景有所差異:
Element.iter()用來尋找 所有 符合要求的 Tag,注意,這里查找的范圍 是所有孩子和孩子的孩子 and so on。如果查看所有的 year,可以使用下面的代碼:

1
2
for neighbor in root.iter('year'):
print neighbor.text

 

返回

2008
2011
2011

Element.findall()只查找 直接的孩子 ,返回所有符合要求的 Tag 的 Element,而Element.find() 只返回符合要求的第一個 Element。如果查看 Singapore 的 year 的值,可以使用下面的代碼:

1
2
3
4
for country in root.findall('country'):
if country.attrib['name'] == 'Singapore':
year = country.find('year') # 使用 Element.find()
print year.text

 

1
2
3
4
for country in root.findall('country'):
if country.attrib['name'] == 'Singapore':
years = country.findall('year') # 使用 Element.findall()
print years[0].text # 注意和上段的區別

查看 Element 的值

我們可以直接用 Element.text 來得到這個 Element 的值。

四、修改 XML

前面已經介紹了如何獲取一個 Element 的對象,以及查看它的 Tag、Attribute、值和它的孩子。下面介紹如何修改一個 Element 並對 XML 文件進行保存

修改 Element

修改 Element 可以直接訪問 Element.text。
修改 Element 的 Attribute,也可以用來新增 Attribute:

Element.set(‘AttributeName’,’AttributeValue’)

新增孩子節點:

Element.append(childElement)

刪除孩子節點:

Element.remove(childElement)

保存 XML

我們從文件解析的時候,我們用了一個 ElementTree 的對象 tree,在完成修改之后,還用 tree 來保存 XML 文件。

1
tree.write('output.xml')

 

構建 XML

ElementTree 提供了兩個靜態函數(直接用類名訪問,這里我們用的是 ET)可以很方便的構建一個 XML,如:

1
2
3
4
5
6
7
root = ET.Element('data') 
country = ET.SubElement(root,'country', {'name':'Liechtenstein'})
rank = ET.SubElement(country,'rank')
rank.text = '1'
year = ET.SubElement(country,'year')
year.text = '2008'
ET.dump(root)

就可以得到

12008

五、XPath 支持

XPath 表達式用來在 XML 中定位 Element,下面給一個例子來說明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import xml.etree.ElementTree as ET

root = ET.fromstring(countrydata)

# Top-level elements
root.findall(".")

# All 'neighbor' grand-children of 'country' children of the top-level
# elements
root.findall("./country/neighbor")

# Nodes with name='Singapore' that have a 'year' child
root.findall(".//year/..[@name='Singapore']")

# 'year' nodes that are children of nodes with name='Singapore'
root.findall(".//*[@name='Singapore']/year")

# All 'neighbor' nodes that are the second child of their parent
root.findall(".//neighbor[2]")

參考

ElementTree 主頁
ElementTree 的函數與類介紹

ElementTree解析

兩種實現

ElementTree生來就是為了處理XML ,它在python標准庫中有兩種實現。

一種是純Python實現,例如: xml.etree.ElementTree 

另外一種是速度快一點的:   xml.etree.cElementTree

盡量使用C語言實現的那種,因為它速度更快,而且消耗的內存更少! 在程序中可以這樣寫:

 

  1. try:  
  2.     import xml.etree.cElementTree as ET  
  3. except ImportError:  
  4.     import xml.etree.ElementTree as ET  

常用方法

  1. # 當要獲取屬性值時,用attrib方法。  
  2. # 當要獲取節點值時,用text方法。  
  3. # 當要獲取節點名時,用tag方法。  

示例XML

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <info>  
  3.    <intro>Book message</intro>  
  4.     <list id='001'>  
  5.         <head>bookone</head>  
  6.         <name>python check</name>  
  7.         <number>001</number>  
  8.         <page>200</page>  
  9.     </list>  
  10.     <list id='002'>  
  11.         <head>booktwo</head>  
  12.         <name>python learn</name>  
  13.         <number>002</number>  
  14.         <page>300</page>  
  15.     </list>  
  16. </info>  

###########

##  加載XML 

###########
方法一:加載文件

  1. root = ET.parse('book.xml')  

方法二:加載字符串

  1. root = ET.fromstring(xmltext)  

 

###########

## 獲取節點

###########
方法一:獲得指定節點->getiterator()方法

  1. book_node = root.getiterator('list')  

方法二:獲得指定節點->findall()方法

  1. book_node = root.findall('list')  

方法三:獲得指定節點->find()方法

  1. book_node = root.find('list')  

方法四:獲得兒子節點->getchildren()

  1. for node in book_node:  
  2.     book_node_child = node.getchildren()[0]  
  3.     print book_node_child.tag, '=> ', book_node_child.text  

###########

##  例子01 

###########

 

  1. # coding=utf-8  
  2.   
  3. try:                                           # 導入模塊  
  4.     import xml.etree.cElementTree as ET  
  5. except ImportError:  
  6.     import xml.etree.ElementTree as ET  
  7.   
  8. root   = ET.parse('book.xml')                 # 分析XML文件  
  9. books  = root.findall('/list')                # 查找所有根目錄下的list的子節點  
  10. for book_list in books:                       # 對查找后的結果遍歷  
  11.     print "=" * 30                            # 輸出格式             
  12.     for book in book_list:                    # 對每個子節點再進行遍歷,找出里面你的屬性及值                       
  13.         if book.attrib.has_key('id'):         # 一句id來做條件判斷  
  14.             print "id:", book.attrib['id']    # 根據id打印出屬性值  
  15.         print book.tag + '=> ' + book.text    # 輸出標簽及文本內容  
  16. print "=" * 30  

 

輸出結果:

    1. ==============================  
    2. head=> bookone  
    3. name=> python check  
    4. number=> 001  
    5. page=> 200  
    6. ==============================  
    7. head=> booktwo  
    8. name=> python learn  
    9. number=> 002  
    10. page=> 300  
    11. ==============================  

Python 標准庫之 xml.etree.ElementTree

 

簡介

 

Element類型是一種靈活的容器對象,用於在內存中存儲結構化數據。

[注意]xml.etree.ElementTree模塊在應對惡意結構數據時顯得並不安全。

每個element對象都具有以下屬性:

  1. tag:string對象,表示數據代表的種類。

  2. attrib:dictionary對象,表示附有的屬性。

  3. text:string對象,表示element的內容。

  4. tail:string對象,表示element閉合之后的尾跡。

  5. 若干子元素(child elements)。

<tag attrib1=1>text</tag>tail
1 2 3 4

創建元素的方法有Element或者SubElement(),前者稱作元素的構建函數(constructor),用以構建任一獨存的元素;后者稱作元素的制造函數(factory function),用以制造某一元素的子元素。

有了一串元素之后,使用ElementTree類來將其打包,把一串元素轉換為xml文件或者從xml文件中解析出來。

若想加快速度,可以使用C語言編譯的API xml.etree.cElementTree。

復制代碼
<?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操作

  • 讀取

#從變量讀取,參數為XML段,返回的是一個根Element對象
root = ET.fromstring(country_data_as_string)

#從xml文件中讀取,用getroot獲取根節點,根節點也是Element對象
tree = ET.parse('file.xml')
root = tree.getroot()
  • 訪問

    • 訪問Element對象的標簽、屬性和值
tag = element.tag
attrib = element.attrib
value = element.text
    • 訪問子節點
#打印根節點的標簽和屬性,獲取
for child in root:
    print(child.tag, child.attrib)
  • 查找操作

    • Element元素迭代子元素:Element.iter("tag"),可以羅列該節點所包含的所有其他節點(element對象)
#打印根節點中所有的neighbor對象的name屬性
for neighbor in root.iter('neighbor'):
    print(neighbor.attrib['name'])
    • Element.findall("tag"):查找當前元素為“tag”的直接子元素
#findall只能用來查找直接子元素,不能用來查找rank,neighbor等element
for country in root.findall('country'):
    rank = country.find('rank').text
    name = country.find('rank').text
    neig = country.find('neighbor').attrib
    print(rank, name,neig)
    • Element.find("tag"):查找為tag的第一個直接子元素
#返回第一個tag為country的element,如沒有,返回None
firstCountry = root.find("country")
print(firstCountry)
  • 創建xml文件

復制代碼
__author__ = 'xua'

import xml.etree.ElementTree as ET
#創建根節點
a = ET.Element("root")
#創建子節點,並添加屬性
b = ET.SubElement(a,"sub1")
b.attrib = {"name":"name attribute"}
#創建子節點,並添加數據
c = ET.SubElement(a,"sub2")
c.text = "test"

#創建elementtree對象,寫文件
tree = ET.ElementTree(a)
tree.write("test.xml")
復制代碼

創建的新文件內容為:<root><sub1 name="name attribute" /><sub2>test</sub2></root>

  • 修改XML文件

    • ElementTree.write("xmlfile"):更新xml文件
    • Element.append():為當前element對象添加子元素(element)
    • Element.set(key,value):為當前element的key屬性設置value值
    • Element.remove(element):刪除為element的節點
復制代碼
#讀取待修改文件
updateTree = ET.parse("test.xml")
root = updateTree.getroot()
#創建新節點並添加為root的子節點
newEle = ET.Element("NewElement")
newEle.attrib = {"name":"NewElement","age":"20"}
newEle.text = "This is a new element"
root.append(newEle)

#修改sub1的name屬性
sub1 = root.find("sub1")
sub1.set("name","New Name")

#修改sub2的數據值
sub2 = root.find("sub2")
sub2.text = "New Value"

#寫回原文件
updateTree.write("test.xml")
復制代碼

更新完的文件為:<root><sub1 name="New Name" /><sub2>New Value</sub2><NewElement age="20" name="NewElement">This is a new element</NewElement></root>

 

總結

 XML的操作比較常見,當然也有很多第三方的庫可以使用,所需要做的操作無非就是常用的讀寫xml文件、元素節點的增刪改查,大家還可以在python官方文檔上學習更多的操作。

https://docs.python.org/3.5/library/xml.etree.elementtree.html 

 

xml源文件格式[例]

 

[html]  view plain  copy
 
 print?
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <framework>  
  3.     <processers>  
  4.         <processer name="AProcesser" file="lib64/A.so"  
  5.             path="/tmp">  
  6.         </processer>  
  7.         <processer name="BProcesser" file="lib64/B.so" value="fordelete">  
  8.         </processer>  
  9.         <processer name="BProcesser" file="lib64/B.so2222222"/>  
  10.           
  11.         <services>  
  12.             <service name="search" prefix="/bin/search?"  
  13.                 output_formatter="OutPutFormatter:service_inc">  
  14.                   
  15.                 <chain sequency="chain1"/>  
  16.                 <chain sequency="chain2"></chain>  
  17.             </service>  
  18.             <service name="update" prefix="/bin/update?">  
  19.                 <chain sequency="chain3" value="fordelete"/>  
  20.             </service>  
  21.         </services>  
  22.     </processers>  
  23. </framework>  


使用庫:

 

xml.etree.ElementTree 

官方文檔地址:http://docs.python.org/library/xml.etree.elementtree.html

 

實現思想:

使用ElementTree,先將文件讀入,解析成樹,之后,根據路徑,可以定位到樹的每個節點,再對節點進行修改,最后直接將其輸出

 

代碼附文檔:

 

[python]  view plain  copy
 
 print?
  1. #!/usr/bin/python  
  2. # -*- coding=utf-8 -*-  
  3. # author : wklken@yeah.net  
  4. # date: 2012-05-25  
  5. # version: 0.1  
  6.   
  7. from xml.etree.ElementTree import ElementTree,Element  
  8.   
  9. def read_xml(in_path):  
  10.     '''''讀取並解析xml文件 
  11.        in_path: xml路徑 
  12.        return: ElementTree'''  
  13.     tree = ElementTree()  
  14.     tree.parse(in_path)  
  15.     return tree  
  16.   
  17. def write_xml(tree, out_path):  
  18.     '''''將xml文件寫出 
  19.        tree: xml樹 
  20.        out_path: 寫出路徑'''  
  21.     tree.write(out_path, encoding="utf-8",xml_declaration=True)  
  22.   
  23. def if_match(node, kv_map):  
  24.     '''''判斷某個節點是否包含所有傳入參數屬性 
  25.        node: 節點 
  26.        kv_map: 屬性及屬性值組成的map'''  
  27.     for key in kv_map:  
  28.         if node.get(key) != kv_map.get(key):  
  29.             return False  
  30.     return True  
  31.   
  32. #---------------search -----  
  33.   
  34. def find_nodes(tree, path):  
  35.     '''''查找某個路徑匹配的所有節點 
  36.        tree: xml樹 
  37.        path: 節點路徑'''  
  38.     return tree.findall(path)  
  39.   
  40.   
  41. def get_node_by_keyvalue(nodelist, kv_map):  
  42.     '''''根據屬性及屬性值定位符合的節點,返回節點 
  43.        nodelist: 節點列表 
  44.        kv_map: 匹配屬性及屬性值map'''  
  45.     result_nodes = []  
  46.     for node in nodelist:  
  47.         if if_match(node, kv_map):  
  48.             result_nodes.append(node)  
  49.     return result_nodes  
  50.   
  51. #---------------change -----  
  52.   
  53. def change_node_properties(nodelist, kv_map, is_delete=False):  
  54.     '''''修改/增加 /刪除 節點的屬性及屬性值 
  55.        nodelist: 節點列表 
  56.        kv_map:屬性及屬性值map'''  
  57.     for node in nodelist:  
  58.         for key in kv_map:  
  59.             if is_delete:   
  60.                 if key in node.attrib:  
  61.                     del node.attrib[key]  
  62.             else:  
  63.                 node.set(key, kv_map.get(key))  
  64.               
  65. def change_node_text(nodelist, text, is_add=False, is_delete=False):  
  66.     '''''改變/增加/刪除一個節點的文本 
  67.        nodelist:節點列表 
  68.        text : 更新后的文本'''  
  69.     for node in nodelist:  
  70.         if is_add:  
  71.             node.text += text  
  72.         elif is_delete:  
  73.             node.text = ""  
  74.         else:  
  75.             node.text = text  
  76.               
  77. def create_node(tag, property_map, content):  
  78.     '''''新造一個節點 
  79.        tag:節點標簽 
  80.        property_map:屬性及屬性值map 
  81.        content: 節點閉合標簽里的文本內容 
  82.        return 新節點'''  
  83.     element = Element(tag, property_map)  
  84.     element.text = content  
  85.     return element  
  86.           
  87. def add_child_node(nodelist, element):  
  88.     '''''給一個節點添加子節點 
  89.        nodelist: 節點列表 
  90.        element: 子節點'''  
  91.     for node in nodelist:  
  92.         node.append(element)  
  93.           
  94. def del_node_by_tagkeyvalue(nodelist, tag, kv_map):  
  95.     '''''同過屬性及屬性值定位一個節點,並刪除之 
  96.        nodelist: 父節點列表 
  97.        tag:子節點標簽 
  98.        kv_map: 屬性及屬性值列表'''  
  99.     for parent_node in nodelist:  
  100.         children = parent_node.getchildren()  
  101.         for child in children:  
  102.             if child.tag == tag and if_match(child, kv_map):  
  103.                 parent_node.remove(child)  
  104.                           
  105.   
  106.   
  107. if __name__ == "__main__":  
  108.       
  109.     #1. 讀取xml文件  
  110.     tree = read_xml("./test.xml")  
  111.       
  112.     #2. 屬性修改  
  113.       #A. 找到父節點  
  114.     nodes = find_nodes(tree, "processers/processer")  
  115.       #B. 通過屬性准確定位子節點  
  116.     result_nodes = get_node_by_keyvalue(nodes, {"name":"BProcesser"})  
  117.       #C. 修改節點屬性  
  118.     change_node_properties(result_nodes, {"age": "1"})  
  119.       #D. 刪除節點屬性  
  120.     change_node_properties(result_nodes, {"value":""}, True)  
  121.       
  122.     #3. 節點修改  
  123.       #A.新建節點  
  124.     a = create_node("person", {"age":"15","money":"200000"}, "this is the firest content")  
  125.       #B.插入到父節點之下  
  126.     add_child_node(result_nodes, a)  
  127.       
  128.     #4. 刪除節點  
  129.        #定位父節點  
  130.     del_parent_nodes = find_nodes(tree, "processers/services/service")  
  131.        #准確定位子節點並刪除之  
  132.     target_del_node = del_node_by_tagkeyvalue(del_parent_nodes, "chain", {"sequency" : "chain1"})  
  133.       
  134.     #5. 修改節點文本  
  135.        #定位節點  
  136.     text_nodes = get_node_by_keyvalue(find_nodes(tree, "processers/services/service/chain"), {"sequency":"chain3"})  
  137.     change_node_text(text_nodes, "new text")  
  138.       
  139.     #6. 輸出到結果文件  
  140.     write_xml(tree, "./out.xml")  
  141.       
  142.    



 

通過main處理后的結果文件:

 

[html]  view plain  copy
 
 print?
    1. <?xml version='1.0' encoding='utf-8'?>  
    2. <framework>  
    3.     <processers>  
    4.         <processer file="lib64/A.so" name="AProcesser" path="/tmp">  
    5.         </processer>  
    6.         <processer age="1" file="lib64/B.so" name="BProcesser">  
    7.             <person age="15" money="200000">this is the firest content</person>  
    8.         </processer>  
    9.         <processer age="1" file="lib64/B.so2222222" name="BProcesser">  
    10.             <person age="15" money="200000">this is the firest content</person>  
    11.         </processer>  
    12.   
    13.         <services>  
    14.             <service name="search" output_formatter="OutPutFormatter:service_inc"  
    15.                 prefix="/bin/search?">  
    16.   
    17.                 <chain sequency="chain2" />  
    18.             </service>  
    19.             <service name="update" prefix="/bin/update?">  
    20.                 <chain sequency="chain3" value="fordelete">new text</chain>  
    21.             </service>  
    22.         </services>  
    23.     </processers>  
    24. </framework>  

 導入ElementTree

 

在使用xml.etree.ElementTree時,一般都按如下導入:

try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

XML是中結構化數據形式,在ET中使用ElementTree代表整個XML文檔,並視其為一棵樹,Element代表這個文檔樹中的單個節點。

ET對象具有多種方法從不同來源導入數據,如下:

復制代碼
#從硬盤的xml文件讀取數據

import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')    #載入數據
root = tree.getroot()    #獲取根節點

#從字符串讀取數據
root = ET.fromstring(country_data_as_string)
復制代碼

[注意]fromstring()是直接獲取string對象中的根節點,因此以上root其實是一個Element。

作為一個Element對象,本身是具有子元素,因此可以直接對Element進行迭代取值:

復制代碼
>>> for child in root:
...   print child.tag, child.attrib
...
country {'name': 'Liechtenstein'}
country {'name': 'Singapore'}
country {'name': 'Panama'}

或者直接使用索引尋找子節點:
>>> root[0][1].text '2008'
復制代碼

 

 Element中的遍歷與查詢

 

Element.iter(tag=None):遍歷該Element所有后代,也可以指定tag進行遍歷尋找。

Element.findall(path):查找當前元素下tag或path能夠匹配的直系節點。

Element.find(path):查找當前元素下tag或path能夠匹配的首個直系節點。

Element.text: 獲取當前元素的text值。

Element.get(key, default=None):獲取元素指定key對應的屬性值,如果沒有該屬性,則返回default值。

 

 Element對象 

 

復制代碼
class xml.etree.ElementTree.Element(tag, attrib={}, **extra)

  tag:string,元素代表的數據種類。
  text:string,元素的內容。
  tail:string,元素的尾形。
  attrib:dictionary,元素的屬性字典。
  
  #針對屬性的操作
  clear():清空元素的后代、屬性、text和tail也設置為None。
  get(key, default=None):獲取key對應的屬性值,如該屬性不存在則返回default值。
  items():根據屬性字典返回一個列表,列表元素為(key, value)。
  keys():返回包含所有元素屬性鍵的列表。
  set(key, value):設置新的屬性鍵與值。

  #針對后代的操作
  append(subelement):添加直系子元素。
  extend(subelements):增加一串元素對象作為子元素。#python2.7新特性
  find(match):尋找第一個匹配子元素,匹配對象可以為tag或path。
  findall(match):尋找所有匹配子元素,匹配對象可以為tag或path。
  findtext(match):尋找第一個匹配子元素,返回其text值。匹配對象可以為tag或path。
  insert(index, element):在指定位置插入子元素。
  iter(tag=None):生成遍歷當前元素所有后代或者給定tag的后代的迭代器。#python2.7新特性
  iterfind(match):根據tag或path查找所有的后代。
  itertext():遍歷所有后代並返回text值。
  remove(subelement):刪除子元素。
復制代碼

  

 ElementTree對象

 

復制代碼
class xml.etree.ElementTree.ElementTree(element=None, file=None)
  element如果給定,則為新的ElementTree的根節點。

  _setroot(element):用給定的element替換當前的根節點。慎用。
  
  # 以下方法與Element類中同名方法近似,區別在於它們指定以根節點作為操作對象。
  find(match)
  findall(match)
  findtext(match, default=None)
  getroot():獲取根節點.
  iter(tag=None)
  iterfind(match)
  parse(source, parser=None):裝載xml對象,source可以為文件名或文件類型對象.
  write(fileencoding="us-ascii"xml_declaration=Nonedefault_namespace=None,method="xml") 
復制代碼

 

 模塊方法

 

復制代碼
xml.etree.ElementTree. Comment ( text=None )

創建一個特別的element,通過標准序列化使其代表了一個comment。comment可以為bytestring或unicode。

 

xml.etree.ElementTree. dump ( elem )

生成一個element tree,通過sys.stdout輸出,elem可以是元素樹或單個元素。這個方法最好只用於debug。

 

xml.etree.ElementTree. fromstring ( text )

text是一個包含XML數據的字符串,與XML()方法類似,返回一個Element實例。

 

xml.etree.ElementTree. fromstringlist ( sequenceparser=None )

從字符串的序列對象中解析xml文檔。缺省parser為XMLParser,返回Element實例。

New in version 2.7.

 

xml.etree.ElementTree. iselement ( element )

檢查是否是一個element對象。

 

xml.etree.ElementTree. iterparse ( sourceevents=Noneparser=None )

將文件或包含xml數據的文件對象遞增解析為element tree,並且報告進度。events是一個匯報列表,如果忽略,將只有end事件會匯報出來。

注意,iterparse()只會在看見開始標簽的">"符號時才會拋出start事件,因此屆時屬性是已經定義了,但是text和tail屬性在那時還沒有定義,同樣子元素也沒有定義,因此他們可能不能被顯示出來。如果你想要完整的元素,請查找end事件。

 

xml.etree.ElementTree. parse ( sourceparser=None )

將一個文件或者字符串解析為element tree。

 

xml.etree.ElementTree. ProcessingInstruction ( targettext=None )

這個方法會創建一個特別的element,該element被序列化為一個xml處理命令。

 

xml.etree.ElementTree. register_namespace ( prefixuri )

注冊命名空間前綴。這個注冊是全局有效,任何已經給出的前綴或者命名空間uri的映射關系會被刪除。

New in version 2.7.

 

xml.etree.ElementTree. SubElement ( parenttagattrib={}**extra )

子元素工廠,創建一個Element實例並追加到已知的節點。

 

xml.etree.ElementTree. tostring ( elementencoding="us-ascii"method="xml" )

生成一個字符串來表示表示xml的element,包括所有子元素。element是Element實例,method為"xml","html","text"。返回包含了xml數據的字符串。

 

xml.etree.ElementTree. tostringlist ( elementencoding="us-ascii"method="xml" )

生成一個字符串來表示表示xml的element,包括所有子元素。element是Element實例,method為"xml","html","text"。返回包含了xml數據的字符串列表。

New in version 2.7.

 

xml.etree.ElementTree. XML ( textparser=None )

從一個字符串常量中解析出xml片段。返回Element實例。

 

xml.etree.ElementTree. XMLID ( textparser=None )

從字符串常量解析出xml片段,同時返回一個字典,用以映射element的id到其自身。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM