python 解析XML(拼合互聯網資料學習整理)

一、首先了解XML

如果你已經瞭解xml,可以跳過這一部分。

xml是一種描述層次結構化數據的通用方法。xml文檔包含由起始和結束標籤(tag)分隔的一個或多個元素(element)。以下也是一個完整的(雖然空洞)xml文件:

<foo>   
</foo>  
這是foo元素的起始標籤
這是foo元素對應的結束標籤。就如寫作、數學或者代碼中需要平衡括號一樣,每一個起始標籤必須有對應的結束標籤來閉合(匹配)。

元素可以嵌套到任意層次。位於foo中的元素bar可以被稱作其子元素

<foo>
  <bar></bar>
</foo>

xml文檔中的第一個元素叫做根元素(root element)。並且每份xml文檔只能有一個根元素。以下不是一個xml文檔,因爲它存在兩個“根元素”。

<foo></foo>
<bar></bar>

元素可以有其屬性(attribute),它們是一些名字-值(name-value)對。屬性由空格分隔列舉在元素的起始標籤中。一個元素中屬性名不能重複。屬性值必須用引號包圍起來。單引號、雙引號都是可以。

<foo lang='en'>                          
  <bar id='papayawhip' lang="fr"></bar>  
</foo>
foo元素有一個叫做lang的屬性。lang的值爲en
bar元素則有兩個屬性,分別爲idlang。其中lang屬性的值爲fr。它不會與foo的那個屬性產生衝突。每個元素都其獨立的屬性集。

如果元素有多個屬性,書寫的順序並不重要。元素的屬性是一個無序的鍵-值對集,跟Python中的列表對象一樣。另外,元素中屬性的個數是沒有限制的。

元素可以有其文本內容(text content)

<foo lang='en'>
  <bar lang='fr'>PapayaWhip</bar>
</foo>

如果某一元素既沒有文本內容,也沒有子元素,它也叫做空元素

<foo></foo>

表達空元素有一種簡潔的方法。通過在起始標籤的尾部添加/字符,我們可以省略結束標籤。上一個例子中的xml文檔可以寫成這樣:

<foo/>

就像Python函數可以在不同的模塊(modules)中聲明一樣,也可以在不同的名字空間(namespace)中聲明xml元素。xml文檔的名字空間通常看起來像URL。我們可以通過聲明xmlns來定義默認名字空間。名字空間聲明跟元素屬性看起來很相似,但是它們的作用是不一樣的。

<feed xmlns='http://www.w3.org/2005/Atom'>  
  <title>dive into mark</title>             
</feed>
feed元素處在名字空間http://www.w3.org/2005/Atom中。
title元素也是。名字空間聲明不僅會作用於當前聲明它的元素,還會影響到該元素的所有子元素。

也可以通過xmlns:prefix聲明來定義一個名字空間並取其名爲prefix。然後該名字空間中的每個元素都必須顯式地使用這個前綴(prefix)來聲明。

<atom:feed xmlns:atom='http://www.w3.org/2005/Atom'>  
  <atom:title>dive into mark</atom:title>             
</atom:feed>
feed元素屬於名字空間http://www.w3.org/2005/Atom
title元素也在那個名字空間。

對於xml解析器而言,以上兩個xml文檔是一樣的。名字空間 + 元素名 = xml標識。前綴只是用來引用名字空間的,所以對於解析器來說,這些前綴名(atom:)其實無關緊要的。名字空間相同,元素名相同,屬性(或者沒有屬性)相同,每個元素的文本內容相同,則xml文檔相同。

最後,在根元素之前,字符編碼信息可以出現在xml文檔的第一行。(這裏存在一個兩難的局面(catch-22),直觀上來說,解析xml文檔需要這些編碼信息,而這些信息又存在於xml文檔中,如果你對xml如何解決此問題有興趣,請參閱xml規範中 F 章節

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

XML 的聲明

<?xml version=”1.0” standalone=”yes” encoding=”UTF-8”?>

    這是一個XML處理指令。處理指令以 <? 開始,以 ?> 結束。<? 後的第一個單詞是指令名,如xml, 代表XML聲明。     version, standalone, encoding 是三個特性,特性是由等號分開的名稱-數值對,等號左邊是特性名稱,等號右邊是特性的值,用引號引起來。 幾點解釋:

  • version: 說明這個文檔符合1.0規範
  • standalone: 說明文檔在這一個文件裏還是需要從外部導入, standalone 的值設爲yes 說明所有的文檔都在這一文件裏完成 
  • encoding: 指文檔字符編碼

XML 根元素定義

XML文檔的樹形結構要求必須有一個根元素。根元素的起始標記要放在所有其它元素起始標記之前,根元素的結束標記根放在其它所有元素的結束標記之後,如

 

<?xml version=”1.0” standalone=”yes” encoding=”UTF-8”?> <Settings> <Person>Zhang San</Person> </Settings>

 

XML元素

元素的基本結構由 開始標記,數據內容,結束標記組成,如

<Person>   <Name>Zhang San</Name>   <Sex>Male</Sex> </Person>

需要注意的是:

  • 元素標記區分大小寫,<Name> <name>是兩個不同的標記
  • 結束標記必須有反斜槓,如 </Name>

XML元素標記命名規則如下:

  • 名字中可以包含字母,數字及其它字母
  • 名字不能以數字或下劃線開頭
  • 名字不能用xml開頭
  • 名字中不能包含空格和冒號

XML中的註釋

XML中註釋如下:

<!-- this is comment -->

需要注意的是:

  • 註釋中不要出現“--”或“-
  • 註釋不要放在標記中
  • 註釋不能嵌套 

5  PI  (Processing Instruction) PI Processing Instruction, 處理指令。PI以“<?”開頭,以“?>”結束,用來給下游的文檔傳遞信息。

<?xml:stylesheet href=”core.css” type=”text/css” ?>

例子表明這個XML文檔用core.css控制顯示。

參考 http://www.cnblogs.com/jb8164/articles/736515.html 簡單講解XML

現在我們已經知道足夠多的xml知識,可以開始探險了! 

二、python操作XML

例如

<?xml version='1.0' encoding='utf-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
 
<title>dive into mark</title>
 
<subtitle>currently between addictions</subtitle>
 
<id>tag:diveintomark.org,2001-07-29:/</id>
 
<updated>2009-03-27T21:56:07Z</updated>
 
<link rel='alternate' type='text/html' href='http://diveintomark.org/'/>
 
<link rel='self' type='application/atom+xml' href='http://diveintomark.org/feed/'/>
 
<entry>
   
<author>
     
<name>Mark</name>
     
<uri>http://diveintomark.org/</uri>
   
</author>
   
<title>Dive into history, 2009 edition</title>
   
<link rel='alternate' type='text/html'
     
href='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/>
   
<id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id>
   
<updated>2009-03-27T21:56:07Z</updated>
   
<published>2009-03-27T17:20:42Z</published>
   
<category scheme='http://diveintomark.org' term='diveintopython'/>
   
<category scheme='http://diveintomark.org' term='docbook'/>
   
<category scheme='http://diveintomark.org' term='html'/>
 
<summary type='html'>Putting an entire chapter on one page sounds
    bloated, but consider this &amp;mdash; my longest chapter so far
    would be 75 printed pages, and it loads in under 5 seconds&amp;hellip;
    On dialup.
</summary>
 
</entry>
 
<entry>
   
<author>
     
<name>Mark</name>
     
<uri>http://diveintomark.org/</uri>
   
</author>
   
<title>Accessibility is a harsh mistress</title>
   
<link rel='alternate' type='text/html'
     
href='http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress'/>
   
<id>tag:diveintomark.org,2009-03-21:/archives/20090321200928</id>
   
<updated>2009-03-22T01:05:37Z</updated>
   
<published>2009-03-21T20:09:28Z</published>
   
<category scheme='http://diveintomark.org' term='accessibility'/>
   
<summary type='html'>The accessibility orthodoxy does not permit people to
      question the value of features that are rarely useful and rarely used.
</summary>
 
</entry>
 
<entry>
   
<author>
     
<name>Mark</name>
   
</author>
   
<title>A gentle introduction to video encoding, part 1: container formats</title>
   
<link rel='alternate' type='text/html'
     
href='http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats'/>
   
<id>tag:diveintomark.org,2008-12-18:/archives/20081218155422</id>
   
<updated>2009-01-11T19:39:22Z</updated>
   
<published>2008-12-18T15:54:22Z</published>
   
<category scheme='http://diveintomark.org' term='asf'/>
   
<category scheme='http://diveintomark.org' term='avi'/>
   
<category scheme='http://diveintomark.org' term='encoding'/>
   
<category scheme='http://diveintomark.org' term='flv'/>
   
<category scheme='http://diveintomark.org' term='GIVE'/>
   
<category scheme='http://diveintomark.org' term='mp4'/>
   
<category scheme='http://diveintomark.org' term='ogg'/>
   
<category scheme='http://diveintomark.org' term='video'/>
   
<summary type='html'>These notes will eventually become part of a
      tech talk on video encoding.
</summary>
 
</entry>
</feed><?xml version='1.0' encoding='utf-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
 
<title>dive into mark</title>
 
<subtitle>currently between addictions</subtitle>
 
<id>tag:diveintomark.org,2001-07-29:/</id>
 
<updated>2009-03-27T21:56:07Z</updated>
 
<link rel='alternate' type='text/html' href='http://diveintomark.org/'/>
 
<link rel='self' type='application/atom+xml' href='http://diveintomark.org/feed/'/>
 
<entry>
   
<author>
     
<name>Mark</name>
     
<uri>http://diveintomark.org/</uri>
   
</author>
   
<title>Dive into history, 2009 edition</title>
   
<link rel='alternate' type='text/html'
     
href='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/>
   
<id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id>
   
<updated>2009-03-27T21:56:07Z</updated>
   
<published>2009-03-27T17:20:42Z</published>
   
<category scheme='http://diveintomark.org' term='diveintopython'/>
   
<category scheme='http://diveintomark.org' term='docbook'/>
   
<category scheme='http://diveintomark.org' term='html'/>
 
<summary type='html'>Putting an entire chapter on one page sounds
    bloated, but consider this &amp;mdash; my longest chapter so far
    would be 75 printed pages, and it loads in under 5 seconds&amp;hellip;
    On dialup.
</summary>
 
</entry>
 
<entry>
   
<author>
     
<name>Mark</name>
     
<uri>http://diveintomark.org/</uri>
   
</author>
   
<title>Accessibility is a harsh mistress</title>
   
<link rel='alternate' type='text/html'
     
href='http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress'/>
   
<id>tag:diveintomark.org,2009-03-21:/archives/20090321200928</id>
   
<updated>2009-03-22T01:05:37Z</updated>
   
<published>2009-03-21T20:09:28Z</published>
   
<category scheme='http://diveintomark.org' term='accessibility'/>
   
<summary type='html'>The accessibility orthodoxy does not permit people to
      question the value of features that are rarely useful and rarely used.
</summary>
 
</entry>
 
<entry>
   
<author>
     
<name>Mark</name>
   
</author>
   
<title>A gentle introduction to video encoding, part 1: container formats</title>
   
<link rel='alternate' type='text/html'
     
href='http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats'/>
   
<id>tag:diveintomark.org,2008-12-18:/archives/20081218155422</id>
   
<updated>2009-01-11T19:39:22Z</updated>
   
<published>2008-12-18T15:54:22Z</published>
   
<category scheme='http://diveintomark.org' term='asf'/>
   
<category scheme='http://diveintomark.org' term='avi'/>
   
<category scheme='http://diveintomark.org' term='encoding'/>
   
<category scheme='http://diveintomark.org' term='flv'/>
   
<category scheme='http://diveintomark.org' term='GIVE'/>
   
<category scheme='http://diveintomark.org' term='mp4'/>
   
<category scheme='http://diveintomark.org' term='ogg'/>
   
<category scheme='http://diveintomark.org' term='video'/>
   
<summary type='html'>These notes will eventually become part of a
      tech talk on video encoding.
</summary>
 
</entry>
</feed>

Python可以使用幾種不同的方式解析xml文檔。它包含了domsax解析器,但是我們焦點將放在另外一個叫做ElementTree的庫上邊。

跳過該代碼清單

 

ElementTree屬於Python標準庫的一部分,它的位置爲xml.etree.ElementTree
parse()函數是ElementTree庫的主要入口,它使用文件名或者流對象作爲參數。parse()函數會立即解析完整個文檔。如果內存資源緊張,也可以增量式地解析xml文檔
parse()函數會返回一個能代表整篇文檔的對象。這不是根元素。要獲得根元素的引用可以調用getroot()方法。
如預期的那樣,根元素即http://www.w3.org/2005/Atom名字空間中的feed。該字符串表示再次重申了非常重要的一點:xml元素由名字空間和標籤名(也稱作本地名(local name))組成。這篇文檔中的每個元素都在名字空間Atom中,所以根元素被表示爲{http://www.w3.org/2005/Atom}feed

 

ElementTree使用{namespace}localname來表達xml元素。我們將會在ElementTree的api中多次見到這種形式。

元素即列表#

在ElementTree API中,元素的行爲就像列表一樣。列表中的項即該元素的子元素。

跳過該代碼清單

# continued from the previous example >>> root.tag '{http://www.w3.org/2005/Atom}feed' >>> len(root) 8 >>> for child in root: ...   print(child) ... <Element {http://www.w3.org/2005/Atom}title at e2b5d0> <Element {http://www.w3.org/2005/Atom}subtitle at e2b4e0> <Element {http://www.w3.org/2005/Atom}id at e2b6c0> <Element {http://www.w3.org/2005/Atom}updated at e2b6f0> <Element {http://www.w3.org/2005/Atom}link at e2b4b0> <Element {http://www.w3.org/2005/Atom}entry at e2b720> <Element {http://www.w3.org/2005/Atom}entry at e2b510> <Element {http://www.w3.org/2005/Atom}entry at e2b750>

 

緊接前一例子,根元素爲{http://www.w3.org/2005/Atom}feed
根元素的“長度”即子元素的個數。
我們可以像使用迭代器一樣來遍歷其子元素。
從輸出可以看到,根元素總共有8個子元素:所有feed級的元數據(titlesubtitleidupdatedlink),還有緊接着的三個entry元素。

 

也許你已經注意到了,但我還是想要指出來:該列表只包含直接子元素。每一個entry元素都有其子元素,但是並沒有包括在這個列表中。這些子元素本可以包括在entry元素的列表中,但是確實不屬於feed的子元素。但是,無論這些元素嵌套的層次有多深,總是有辦法定位到它們的;在這章的後續部分我們會介紹兩種方法。

屬性即字典#

xml不只是元素的集合;每一個元素還有其屬性集。一旦獲取了某個元素的引用,我們可以像操作Python的字典一樣輕鬆獲取到其屬性。

跳過該代碼清單

# continuing from the previous example >>> root.attrib {'{http://www.w3.org/XML/1998/namespace}lang': 'en'} >>> root[4] <Element {http://www.w3.org/2005/Atom}link at e181b0> >>> root[4].attrib {'href': 'http://diveintomark.org/',  'type': 'text/html',  'rel': 'alternate'} >>> root[3] <Element {http://www.w3.org/2005/Atom}updated at e2b4e0> >>> root[3].attrib {}

 

attrib是一個代表元素屬性的字典。這個地方原來的標記語言是這樣描述的:<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>。前綴xml:指示一個內置的名字空間,每一個xml不需要聲明就可以使用它。
第五個子元素 — 以0爲起始的列表中即[4] — 爲元素link
link元素有三個屬性:hreftype,和rel
第四個子元素 — [3] — 爲updated

元素updated沒有子元素,所以.attrib是一個空的字典對象。

 

元素爲列表,可以用片段操作符操作,

屬性爲字典,有鍵和值。

 

在XML文檔中查找結點#

到目前爲止,我們已經“自頂向下“地從根元素開始,一直到其子元素,走完了整個文檔。但是許多情況下我們需要找到xml中特定的元素。Etree也能完成這項工作。

跳過該代碼清單

>>> import xml.etree.ElementTree as etree >>> tree = etree.parse('examples/feed.xml') >>> root = tree.getroot() >>> root.findall('{http://www.w3.org/2005/Atom}entry') [<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>, <Element {http://www.w3.org/2005/Atom}entry at e2b510>, <Element {http://www.w3.org/2005/Atom}entry at e2b540>] >>> root.tag '{http://www.w3.org/2005/Atom}feed' >>> root.findall('{http://www.w3.org/2005/Atom}feed') [] >>> root.findall('{http://www.w3.org/2005/Atom}author') []

 

findfall()方法查找匹配特定格式的子元素。(關於查詢的格式稍後會講到。)
每個元素 — 包括根元素及其子元素 — 都有findall()方法。它會找到所有匹配的子元素。但是爲什麼沒有看到任何結果呢?也許不太明顯,這個查詢只會搜索其子元素。由於根元素feed中不存在任何叫做feed的子元素,所以查詢的結果爲一個空的列表。
這個結果也許也在你的意料之外。在這篇文檔中確實存在author元素;事實上總共有三個(每個entry元素中都有一個)。但是那些author元素不是根元素的直接子元素。我們可以在任意嵌套層次中查找author元素,但是查詢的格式會有些不同。

 

跳過該代碼清單

>>> tree.findall('{http://www.w3.org/2005/Atom}entry') [<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>, <Element {http://www.w3.org/2005/Atom}entry at e2b510>, <Element {http://www.w3.org/2005/Atom}entry at e2b540>] >>> tree.findall('{http://www.w3.org/2005/Atom}author') []

 

爲了方便,對象tree(調用etree.parse()的返回值)中的一些方法是根元素中這些方法的鏡像。在這裏,如果調用tree.getroot().findall(),則返回值是一樣的。
也許有些意外,這個查詢請求也沒有找到文檔中的author元素。爲什麼沒有呢?因爲它只是tree.getroot().findall('{http://www.w3.org/2005/Atom}author')的一種簡潔表示,即“查詢所有是根元素的子元素的author”。因爲這些authorentry元素的子元素,所以查詢沒有找到任何匹配的。

 

find()方法用來返回第一個匹配到的元素。當我們認爲只會有一個匹配,或者有多個匹配但我們只關心第一個的時候,這個方法是很有用的。

跳過該代碼清單

 

在前一樣例中已經看到。這一句返回所有的atom:entry元素。
find()方法使用ElementTree作爲參數,返回第一個匹配到的元素。
entries[0]中沒有叫做foo的元素,所以返回值爲None

 

可逮住你了,在這裏find()方法非常容易被誤解。在布爾上下文中,如果ElementTree元素對象不包含子元素,其值則會被認爲是False如果len(element)等於0)。這就意味着if element.find('...')並非在測試是否find()方法找到了匹配項;這條語句是在測試匹配到的元素是否包含子元素!想要測試find()方法是否返回了一個元素,則需使用if element.find('...') is not None

可以在所有派生(descendant)元素中搜索,任意嵌套層次的子元素,孫子元素等…

跳過該代碼清單

>>> all_links = tree.findall('//{http://www.w3.org/2005/Atom}link') >>> all_links [<Element {http://www.w3.org/2005/Atom}link at e181b0>, <Element {http://www.w3.org/2005/Atom}link at e2b570>, <Element {http://www.w3.org/2005/Atom}link at e2b480>, <Element {http://www.w3.org/2005/Atom}link at e2b5a0>] >>> all_links[0].attrib {'href': 'http://diveintomark.org/',  'type': 'text/html',  'rel': 'alternate'} >>> all_links[1].attrib {'href': 'http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition',  'type': 'text/html',  'rel': 'alternate'} >>> all_links[2].attrib {'href': 'http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress',  'type': 'text/html',  'rel': 'alternate'} >>> all_links[3].attrib {'href': 'http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats',  'type': 'text/html',  'rel': 'alternate'}

 

//{http://www.w3.org/2005/Atom}link與前一樣例很相似,除了開頭的兩條斜線。這兩條斜線告訴findall()方法“不要只在直接子元素中查找;查找的範圍可以是任意嵌套層次”。
查詢到的第一個結果根元素的直接子元素。從它的屬性中可以看出,它是一個指向該feed的html版本的備用鏈接。
其他的三個結果分別是低一級的備用鏈接。每一個entry都有單獨一個link子元素,由於在查詢語句前的兩條斜線的作用,我們也能定位到他們。

 

總的來說,ElementTree的findall()方法是其一個非常強大的特性,但是它的查詢語言卻讓人有些出乎意料。官方描述它爲“有限的XPath支持。”XPath是一種用於查詢xml文檔的W3C標準。對於基礎地查詢來說,ElementTree與XPath語法上足夠相似,但是如果已經會XPath的話,它們之間的差異可能會使你感到不快。現在,我們來看一看另外一個第三方xml庫,它擴展了ElementTree的api以提供對XPath的全面支持。

重點:

用根元素,這樣可以查找任意元素。

查找第一個節點,用find

查找一個元素的所有直接子元素findall()

加上兩條斜線。這兩條斜線告訴findall()方法“不要只在直接子元素中查找;查找的範圍可以是任意嵌套層次”。

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值。

參考:

http://www.cnblogs.com/ifantastic/archive/2013/04/12/3017110.html   Python標準庫之xml.etree.ElementTree
http://www.w3school.com.cn/xmldom/dom_methods.asp  XML DOM - 屬性和方法
http://blog.csdn.net/yueguanghaidao/article/details/7265246 例子
http://blog.csdn.net/menglei8625/article/details/7494509  Python_使用ElementTree解析xml文件
http://www.cnblogs.com/jb8164/articles/736515.html 簡單講解XML
http://woodpecker.org.cn/diveintopython3/xml.html  系統講XML解析和python解析 不錯


 

 

發佈了29 篇原創文章 · 獲贊 9 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章