當前位置:成語大全網 - 英語詞典 - 深入解讀Python解析XML的幾種方式

深入解讀Python解析XML的幾種方式

在XML解析方面,Python實現了自己的“包含電池”原則。在自己的標準庫中,Python提供了大量可以用來處理XML語言的包和工具,數量之多,甚至連Python編程的初學者都無從選擇。

本文將介紹幾種使用Python語言深度解讀XML文件的方法,並以筆者推薦的ElementTree模塊為例,演示具體的使用方法和場景。本文使用的Python版本是2.7。

1.什麽是XML?

XML是可擴展標記語言的縮寫,其中標記是關鍵部分。您可以創建內容,然後用限定標簽對其進行標記,以便每個單詞、短語或塊都成為可識別和可分類的信息。

標記語言已經從私人公司和政府的早期形式逐漸演變成標準的通用標記語言(SGML)、超文本標記語言(HTML),並最終演變成XML。XML具有以下特征。

XML被設計用來傳輸數據,而不是顯示數據。

XML標記不是預定義的。妳需要定義妳自己的標簽。

XML被設計成自描述的。

XML是W3C的推薦標準。

目前,XML在Web中扮演的角色不亞於HTML,它壹直是Web的基石。XML無處不在。XML是各種應用程序之間最常用的數據傳輸工具,在信息存儲和描述領域也越來越流行。因此,學習如何解析XML文件對於Web開發來說是非常重要的。

2.有哪些Python包可以解析XML?

Python的標準庫提供了六個可用於處理XML的包。

xml.dom

Xml.dom實現了W3C開發的DOM API。如果妳習慣使用DOM API或者有人要求妳這麽做,妳可以使用這個包。但是需要註意的是,在這個包中,還提供了幾個不同的模塊,它們的性能是不同的。

DOM解析器必須在任何處理開始之前將基於XML文件生成的樹數據放入內存,所以DOM解析器的內存使用完全取決於輸入數據的大小。

xml.dom.minidom

Xml.dom.minidom是壹個非常簡化的dom API實現,比完整版的DOM簡單很多,包也小很多。不熟悉DOM的人應該考慮使用xml.etree.ElementTree模塊。根據筆者對lxml的評價,這個模塊使用起來不方便,效率低,容易出問題。

xml.dom.pulldom

與其他模塊不同的是,xml.dom.pulldom模塊提供了壹個“拉解析器”,其背後的基本概念是指從xml流中拉出事件,然後進行處理。盡管事件驅動的處理模型與SAX相同,但不同之處在於,當使用pull解析器時,用戶需要從XML流中顯式地提取事件並處理它們,直到處理完成或出現錯誤。

拉解析是XML處理的最新趨勢。以前流行的XML解析框架如SAX、DOM都是基於推送的,也就是說解析工作的控制權在解析器手裏。

xml.sax

xml.sax模塊實現SAX API,犧牲了速度和內存使用的便利性。SAX是Simple API for XML的縮寫,不是W3C官方提出的標準。它是事件驅動的,不需要壹次性讀取整個文檔,文檔的讀取過程也是SAX的解析過程。事件驅動是指壹種基於回調機制的程序運行方法。

xml.parser.expat

Xml.parser.expat為用C語言編寫的expat解析器提供了壹個直接的低級API接口。類似於SAX,expat接口是基於事件回調機制的,但是這個接口沒有標準化,只適用於expat庫。

Expat是壹個面向流的解析器。註冊解析器回調(或處理程序)函數,然後開始搜索它的文檔。當解析器識別出文件的指定位置時,它將為該部分調用相應的處理程序(如果您已經註冊了壹個)。文件被發送到解析器,解析器將被分成多個片段並加載到內存中。這樣expat就可以解析那些巨大的文件。

Xml。ETree.ElementTree(以下簡稱et)

xml.etree.ElementTree模塊提供了壹個輕量級的Pythonic API和壹個高效的C語言實現,即xml.etree.cElementTree..與DOM相比,ET速度更快,API使用起來更直接方便。與SAX相比,ET.iterparse函數還提供了按需解析的功能,不會壹次性讀取內存中的整個文檔。ET的性能和SAX模塊差不多,但是它的API更高,更方便用戶使用。

我建議在使用Python進行XML解析時,首先選擇ET模塊,除非妳有其他特殊需求,可能需要另外壹個模塊來滿足。

這些解析XML的API並不是Python獨創的,Python也是從其他語言或者直接從其他語言引入的。比如expat就是壹個用C語言開發的用於解析XML文檔的開發庫。SAX最初是由DavidMegginson使用java語言開發的。DOM可以以獨立於平臺和語言的方式訪問和修改文檔的內容和結構,可以應用於任何編程語言。

接下來我們以ElementTree模塊為例,介紹如何用Python解析lxml。

第三,使用ElementTree解析XML

Python標準庫提供了ET的兩種實現。壹個是純Python實現的xml.etree.ElementTree,壹個是xml。用更快的C語言實現的etree.elementtree。記得壹定要用C語言,因為它速度快得多,占用內存也少得多。如果您使用的Python版本沒有cElementTree所需的加速模塊,您可以像這樣導入模塊:

嘗試:

將xml.etree.cElementTree作為ET導入

除了導入錯誤:

將xml.etree.ElementTree作為ET導入

如果壹個API有不同的實現,上面是壹個通用的導入方法。當然,直接導入第壹個模塊很可能不會有問題。請註意,從Python 3.3開始,不需要上述導入方法,因為ElemenTree模塊會自動先使用C加速器,如果沒有C實現,就使用Python實現。所以使用Python 3.3+的朋友只需要導入xml.etree.ElementTree即可。

1.將XML文檔解析成樹。

讓我們從基礎開始。XML是壹種結構化和層次化的數據格式,而最適合體現XML的數據結構是樹。ET提供了兩個對象:ElementTree將整個XML文檔轉換成壹棵樹,Element表示樹上的單個節點。整個XML文檔的交互(讀、寫、搜索所需元素)壹般都是在ElementTree級別進行的。對於單個XML元素及其子元素,這是在元素級別完成的。下面用實例介紹壹下主要的使用方法。

我們使用以下XML文檔作為演示數據:

文本,來源

xml,sgml

接下來,我們加載並解析這個文檔:

& gt& gt& gt將xml.etree.ElementTree作為ET導入

& gt& gt& gttree = ET。element tree(file = ' doc 1 . XML ')

然後,我們得到根元素:

& gt& gt& gttree.getroot()

如前所述,根元素是壹個元素對象。讓我們看看根元素有什麽屬性:

& gt& gt& gtroot = tree.getroot()

& gt& gt& gt根.標簽,根.屬性

(' doc ',{})

是的,根元素沒有屬性。像其他元素對象壹樣,根元素有壹個接口來遍歷它的直接子元素:

& gt& gt& gt對於根中的根的子代:

...print child_of_root.tag

...

branch {'hash': '1cdf045c ',' name': 'codingpy.com'}

分支{'hash': 'f200013e ',' name': 'release01'}

分支{ '名稱':'無效' }

我們還可以通過索引值訪問特定的子元素:

& gt& gt& gt根[0]。標記,根[0]。文本

('分支','

文本,來源

)

2.找到所需的元素。

從上面的例子中,很明顯,我們可以通過壹個簡單的遞歸方法(遞歸訪問每個元素的所有子元素)獲得樹中的所有元素。但是因為這是壹個很普通的工作,所以ET提供了壹些簡單的實現方法。

element對象有壹個iter方法,可以對Element對象下的所有子元素執行深度優先遍歷(DFS)。ElementTree對象也有這個方法。下面是查找XML文檔中所有元素的最簡單方法:

& gt& gt& gt對於tree.iter()中的elem:

...打印elem.tag

...

文檔{}

branch {'hash': '1cdf045c ',' name': 'codingpy.com'}

分支{'hash': 'f200013e ',' name': 'release01'}

支行{'name': 'subrelease01'}

分支{ '名稱':'無效' }

在此基礎上,我們可以隨意遍歷樹——遍歷所有的元素,找出我們感興趣的屬性。但是ET可以讓這個工作變得更容易更快。iter方法可以接受標記名,然後使用提供的標記遍歷所有元素:

& gt& gt& gt對於tree.iter中的elem(tag = ' branch '):

...打印elem.tag

...

branch {'hash': '1cdf045c ',' name': 'codingpy.com'}

分支{'hash': 'f200013e ',' name': 'release01'}

分支{ '名稱':'無效' }

3.支持通過XPath查找元素。

使用XPath查找感興趣的元素更方便。Element對象中有壹些find方法可以接受Xpath路徑作為參數。find方法將返回第壹個匹配的子元素,findall將以列表的形式返回所有匹配的子元素,iterfind將返回所有匹配元素的叠代器。ElementTree對象也有這些方法,因此它的搜索從根節點開始。

以下是使用XPath查找元素的示例:

& gt& gt& gt對於tree.iterfind中的elem('分支/子分支'):

...打印elem.tag

...

支行{'name': 'subrelease01'}

上面的代碼返回branch元素下所有標記為sub-branch的元素。接下來,查找具有特定name屬性的所有分支元素:

& gt& gt& gt對於tree . ITER find(' branch[@ name = " release 01 "]')中的elem:

...打印elem.tag

...

分支{'hash': 'f200013e ',' name': 'release01'}

4.構建XML文檔

使用ET,很容易構建壹個XML文檔並將其寫成壹個文件。ElementTree對象的write方法可以滿足這壹要求。

壹般來說,主要有兩種使用場景。首先,您讀取壹個XML文檔,進行更改,然後將更改寫入文檔。其次,從頭開始創建壹個新的XML文檔。

修改文檔可以通過調整元素對象來實現。請看下面的例子:

& gt& gt& gtroot = tree.getroot()

& gt& gt& gt德爾根[2]

& gt& gt& gt根[0]。設置(' foo ',' bar ')

& gt& gt& gt對於根中的子木質部:

...打印subelem.tag,subelem.attrib

...

branch {'foo': 'bar ',' hash': '1cdf045c ',' name': 'codingpy.com'}

分支{'hash': 'f200013e ',' name': 'release01'}

在上面的代碼中,我們刪除了根元素的第三個子元素,並向第壹個子元素添加了新的屬性。這個樹可以重寫到文件中。最終的XML文檔應該如下所示:

& gt& gt& gt導入系統

& gt& gt& gttree.write(sys.stdout)

文本,來源

xml,sgml

請註意,文檔中元素的屬性順序與原始文檔不同。這是因為ET以字典的形式存儲屬性,這是壹種無序的數據結構。當然,XML並不關註屬性的順序。

從頭開始構建壹個完整的文檔也很容易。ET模塊提供了子元素工廠功能,這使得創建元素的過程非常簡單:

& gt& gt& gta = ET。元素(“elem”)

& gt& gt& gtc = ET。子元素(a,“child1”)

& gt& gt& gtc.text = "some text "

& gt& gt& gtd = ET。子元素(a,“子元素2”)

& gt& gt& gtb = ET。元素(' elem_b ')

& gt& gt& gtroot = ET。元素(“根”)

& gt& gt& gtroot.extend((a,b))

& gt& gt& gttree = ET。元素樹(根)

& gt& gt& gttree.write(sys.stdout)

壹些文本

5.使用iterparse解析XML流。

XML文檔通常相對較大。如果直接把文檔讀入內存,解析會有問題。這也是不推薦DOM,而推薦SAX API的原因之壹。

正如我們上面提到的,ET可以將XML文檔作為內存樹加載,然後處理它。但是解析大文件的時候,應該也和DOM壹樣有內存消耗大的問題吧?是的,有這個問題。為了解決這個問題,ET提供了壹個類似於SAX-ITER解析的特殊工具,可以對XML進行順序解析。

接下來,作者將向您展示如何使用iterparse,並將其與標準的樹解析方法進行比較。我們使用自動生成的XML文檔。以下是文件的開頭:

美國

1

貞潔的九壹八

信用卡

[...]

讓我們統計壹下文檔中出現了多少個文本值為津巴布韋的位置元素。以下是使用ET.parse的標準方法:

tree = ET.parse(sys.argv[2])

計數= 0

對於tree.iter中的elem(tag = ' location '):

if elem.text == '津巴布韋':

計數+= 1

打印計數

上面的代碼將所有元素加載到內存中,並逐個解析它們。當解析壹個大約100MB的XML文檔時,運行上述腳本的Python進程的峰值內存使用量大約為560MB,總運行時間為2.9秒。

請註意,我們並不真的需要討論將整棵樹加載到內存中。只要文本被檢測為具有相應值的位置元素。其他數據可以被丟棄。此時,我們可以使用iterparse方法:

計數= 0

對於event,ET.iterparse(sys.argv[2])中的elem:

if event == 'end ':

如果elem.tag == 'location '且elem.text == '津巴布韋':

計數+= 1

Elem.clear() #丟棄元素。

打印計數

上面的for循環將遍歷iterparse事件,首先檢查事件是否結束,然後判斷元素的標簽是否為location,其文本值是否符合目標值。另外,調用elem.clear()是至關重要的:iterparse還是會生成壹棵樹,只是順序生成。丟棄不必要的元素相當於丟棄整棵樹,釋放系統分配的內存。

用上面的腳本解析同壹個文件時,內存使用峰值只有7MB,運行時間2.5秒。速度提高的原因是,我們只在樹建立時遍歷壹次。使用parse的標準方法是在再次遍歷所需元素之前完成整個樹的構造。

iterparse的性能與SAX相當,但它的API更有用:iterparse會按順序構建樹;使用SAX時,必須自己構建樹。