1. 程式人生 > >使用Python解析XML

使用Python解析XML

XML是可拓展標記語言,用來傳輸和儲存資料

解析XML的三種方法

常見的XML程式設計介面有DOM和SAX,Python有三種方法解析XML:SAX, DOM,ElementTree。

  • SAX means simple API for XML:
    python標準庫包含SAX解析器,用事件驅動模型,通過在解析XML過程中觸發一個個事件並呼叫使用者定義的回撥函式來處理XML檔案。
  • DOM
    將XML資料在記憶體中解析成一個樹,通過對樹的操作來操作XML
  • ElementTree:
    就像一個輕量級的DOM,具有很好的API,程式碼可用性好,速度快,消耗的記憶體小

!!!這裡我們首選使用xml.etree.ElementTree模組


使用ElementTree解析XML:

xml.etree.ElementTree模組提供了一個輕量級API,與DOM相比,ET的速度更快,API使用更直接方便。與SAX相比,ET.iterparse函式同樣提供了按需解析的功能,不會一次性在記憶體中讀入整個文件。ET的效能與SAX模組大致相仿,但是它的API更加高層次,使用者使用起來更加便捷。

在標準庫中提供了對ET的兩種實現,一種是純python版本,一種是c語言實現的版本,這裡使用c語言版本更好,因為它的速度更快,消耗記憶體更少,所以這樣匯入模組:

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

Python3.3之後,不需要這樣匯入,因為ET模組會自動優先使用C加速器

將XML文件解析成樹:
!!!這裡有兩種物件,一個為ElementTree物件,另一個為Element物件
XML文件例子:

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

載入這個文件並且解析:

>>> import xml.etree.ElementTree as ET
>>> tree = ET.ElementTree(file='doc1.xml')  # 載入這個文件並且解析,賦值為tree
---------------------------
>>> tree.getroot()  # 獲取這個樹的根元素
<Element 'doc' at 0x11eb780>
---------------------------
>>> root = tree.getroot()
>>> root.tag, root.attrib  # 檢視這個根元素的屬性,根元素為一個Element物件
('doc', {})  # 根元素並沒有屬性
---------------------------
>>> for child_of_root in root:  # 根元素具備遍歷其直接子元素的介面
...   print child_of_root.tag, child_of_root.attrib  # .tag就是標籤名
...
branch {'hash': '1cdf045c', 'name': 'codingpy.com'}
branch {'hash': 'f200013e', 'name': 'release01'}
branch {'name': 'invalid'}
---------------------------
>>> root[0].tag, root[0].text  # 也可以通過索引值來訪問特定的子元素
('branch', '\n        text,source\n    ')

查詢元素:
ElementElementTree物件都有iter方法,可以對物件之下的所有子元素進行深度優先遍歷DFS,所以這裡最簡單的辦法是:

>>> for elem in tree.iter():
...   print elem.tag, elem.attrib
...
doc {}
branch {'hash': '1cdf045c', 'name': 'codingpy.com'}
branch {'hash': 'f200013e', 'name': 'release01'}
sub-branch {'name': 'subrelease01'}  # 這個是上面一條的子元素,所以這是深度優先遍歷
branch {'name': 'invalid'}

這個方法還能接受tag引數,然後遍歷標籤為這個tag的元素:

>>> for elem in tree.iter(tag='branch'):
...   print elem.tag, elem.attrib

使用XPath查詢元素更加方便:
Element物件中有一些find方法可以接受Xpath路徑作為引數,find方法會返回第一個匹配的子元素,findall會以列表的形式返回所有匹配的子元素,iterfind則返回一個所有匹配元素的迭代器。
ElementTree也有上述方法,查詢就是從根節點開始的。

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

上面是根據路徑查詢
下面是查詢所有具備某個name屬性的元素:

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

修改或者建立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': 'codingpy.com'}
branch {'hash': 'f200013e', 'name': 'release01'}

建立XML文件使用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:

因為ET是將文件載入成樹儲存到記憶體裡面,所以一旦XML很大,也會遇到記憶體消耗太大的問題,這裡需要用到ET裡面類似SAX的特殊工具iterparse,它可以循序的解析XML。

這裡只是介紹有這麼個東西,暫時不介紹如何使用,筆者用到了再更新~