目次
1. 在使用 Python 讀取 XML 之前需要知道的事項
使用 Python 處理 XML 的場合是什麼?
Python 是一種廣泛用於多種用途的通用程式語言,其中「讀取 XML 檔案」是資料處理現場中常用的一項技術。特別是在以下情況下,需要使用 Python 讀取 XML。- 想要解析從 Web API 取得的 XML 資料
- 想要處理從其他系統匯出的 XML 檔案
- 想要讀取作為設定檔案使用的 XML
XML 是什麼?簡單回顧
XML(Extensible Markup Language)是一種可以靈活定義資料結構的標記語言。雖然結構類似 HTML,但用途不同。HTML 用於「外觀(顯示)」,而 XML 是用來描述「資料結構和意義」的格式。 例如,以下這種形式是典型的 XML:<book>
<title>Python 入門</title>
<author>Yamada Tarō</author>
<price>2800</price>
</book>要讀取並使用這種形式的資料,需要使用專用的程式庫。介紹用於在 Python 中讀取 XML 的程式庫
Python 中,標準程式庫和外部程式庫提供了幾種讀取 XML 的方法。代表性的如下。xml.etree.ElementTree(標準程式庫)xml.dom.minidom(標準程式庫)lxml(外部程式庫,支援 XPath 和驗證)
Ad
2. 使用 Python 讀取 XML 的基本知識
使用 ElementTree 讀取 XML 檔案
首先,使用實際的 XML 檔案,讓我們看看使用ElementTree 讀取的方法。 範例 XML(sample.xml):<books>
<book>
<title>Python 入門</title>
<author>Yamada Taro</author>
<price>2800</price>
</book>
<book>
<title>AI 的未來</title>
<author>Suzuki Ichiro</author>
<price>3500</price>
</book>
</books>Python 程式碼:import xml.etree.ElementTree as ET
# 讀取 XML 檔案
tree = ET.parse('sample.xml')
root = tree.getroot()
# 確認根元素標籤
print(f"根元素: {root.tag}")
# 迴圈處理每個 book 元素
for book in root.findall('book'):
title = book.find('title').text
author = book.find('author').text
price = int(book.find('price').text)
print(f"標題: {title}, 作者: {author}, 價格: {price}日圓")執行結果:根元素: books
標題: Python 入門, 作者: Yamada Taro, 價格: 2800日圓
標題: AI 的未來, 作者: Suzuki Ichiro, 價格: 3500日圓就像這樣,使用 ElementTree.parse() 讀取 XML 檔案,使用 getroot() 取得根元素,然後使用 find() 或 findall() 抽出必要的元素,這就是基本的流程。從字串讀取 XML 的方法
XML 不是檔案而是字串的情況也時有發生。那樣的情況下,使用ET.fromstring() 來解析。 範例:xml_data = '''
<user>
<name>Sagawa Shota</name>
<email>sagawa@example.com</email>
</user>
'''
root = ET.fromstring(xml_data)
name = root.find('name').text
email = root.find('email').text
print(f"姓名: {name}, 電子郵件: {email}")同樣地,從根元素使用 find() 取得必要子元素並取出值。屬性的取得與文字的處理
XML 元素中,有時會在標籤內定義屬性。在 Python 中,可以使用.attrib 來存取。 範例(帶屬性的 XML):<user id="101">
<name>Sagawa Shota</name>
</user>Python 程式碼:root = ET.fromstring('''
<user id="101">
<name>Sagawa Shota</name>
</user>
''')
print(f"使用者 ID: {root.attrib['id']}")3. 其他 XML 解析函式庫的介紹
minidom:基於 DOM 的標準函式庫
xml.dom.minidom 是 Python 標準包含的 XML 解析器,基於 W3C 的 DOM(Document Object Model)規格來處理 XML。 與 ElementTree 相比,有時會給人較難處理的印象,但如果想細緻操作節點的種類或結構,則很方便。 範例:from xml.dom import minidom
xml_data = '''
<user>
<name>Sagawa Shota</name>
<email>sagawa@example.com</email>
</user>
'''
dom = minidom.parseString(xml_data)
name = dom.getElementsByTagName('name')[0].firstChild.nodeValue
email = dom.getElementsByTagName('email')[0].firstChild.nodeValue
print(f"名字: {name}, 電子郵件: {email}")特點與優點:- 容易存取細部的節點結構
- 屬性或子節點的類型明確分類
- 容易將 XML 格式化輸出(pretty print)
- 程式碼容易變得冗長
- 不適合處理大型 XML(記憶體消耗大)
lxml:高速且強大的外部函式庫
lxml 是以 C 語言為基礎實作的高速 XML 解析器,對應 XPath 或 XSLT 等進階 XML 功能。由於提供類似 ElementTree 的 API,學習成本也較低。 安裝方法:pip install lxml基本用法:from lxml import etree
xml_data = '''
<books>
<book>
<title>Python 實踐</title>
<price>3000</price>
</book>
</books>
'''
root = etree.fromstring(xml_data)
title = root.xpath('//book/title/text()')[0]
price = root.xpath('//book/price/text()')[0]
print(f"標題: {title}, 價格: {price}日圓")特點與優點:- 可以使用 XPath,因此能進行彈性的搜尋
- 高速,適合大量 XML 處理
- 也具備與 HTML 的相容性,可應用於網頁抓取
- 需要安裝外部函式庫
- 需要一些初始學習(XPath 等)
函式庫的選擇方式總結
| 函式庫 | 特點 | 適合的案例 |
|---|---|---|
| ElementTree | 可標準使用,能進行基本的讀寫 | 小型・中型 XML 的讀取 |
| minidom | 擅長 DOM 操作,格式化輸出 | 想細緻操作節點結構時 |
| lxml | 高速・XPath 對應,彈性高 | 大型資料,需要進階搜尋時 |

Ad
4. 實用的範例程式碼
本節將以接近實際業務或資料處理的形式,實踐性地介紹使用 Python 讀取 XML 的處理方式。具體來說,將展示對應常用模式如「多個節點的重複處理」「依條件過濾」「寫入 XML 檔案」等的程式碼範例。多個節點的重複處理
當 XML 內重複出現相同結構的資料(例如多個<book> 元素)時,使用 findall() 進行迴圈處理。 範例 XML:<books>
<book>
<title>Python 入門</title>
<author>Yamada Tarō</author>
<price>2800</price>
</book>
<book>
<title>AI 的未來</title>
<author>Suzuki Ichirō</author>
<price>3500</price>
</book>
</books>Python 程式碼(使用 ElementTree):import xml.etree.ElementTree as ET
tree = ET.parse('books.xml')
root = tree.getroot()
for book in root.findall('book'):
title = book.find('title').text
author = book.find('author').text
price = int(book.find('price').text)
print(f"標題: {title}, 作者: {author}, 價格: {price} 日圓")透過在迴圈內存取個別元素,即可從 XML 中提取資料並進行處理。依條件的過濾
接下來,介紹僅提取價格 3000 日圓以上的書籍的條件處理方式。 Python 程式碼:for book in root.findall('book'):
price = int(book.find('price').text)
if price >= 3000:
title = book.find('title').text
print(f"高額書籍: {title}({price} 日圓)")透過結合 if 陳述式,即可處理符合任意條件的元素。寫入 XML 檔案(儲存)
對讀取的 XML 進行修改後,以新檔案形式儲存的情境也很常見。 寫出範例:# 建立新的根元素
root = ET.Element('users')
# 新增子元素
user1 = ET.SubElement(root, 'user', attrib={'id': '1'})
ET.SubElement(user1, 'name').text = 'Sagawa Shōta'
ET.SubElement(user1, 'email').text = 'sagawa@example.com'
# 以樹狀結構儲存
tree = ET.ElementTree(root)
tree.write('users.xml', encoding='utf-8', xml_declaration=True)由此,將產生以下的 XML 檔案:<?xml version='1.0' encoding='utf-8'?>
<users>
<user id="1">
<name>Sagawa Shōta</name>
<email>sagawa@example.com</email>
</user>
</users>應用:使用 XPath 提取(lxml)
若使用lxml,則可進行更靈活且強大的搜尋。from lxml import etree
tree = etree.parse('books.xml')
titles = tree.xpath('//book[price >= 3000]/title/text()')
for title in titles:
print(f"高額書籍標題: {title}")活用 XPath,即使是複雜條件也能直覺地提取資料。5. 常見錯誤與對策
在 Python 中讀取 XML 時,可能會發生語法錯誤或字元編碼問題等各種錯誤。本節將介紹初學者容易遇到的典型錯誤及其對策。UnicodeDecodeError:因字元編碼差異導致的讀取失敗
錯誤內容:UnicodeDecodeError: 'utf-8' codec can't decode byte 0x93 in position 10原因: 如果 XML 檔案是以 UTF-8 以外的字元編碼(例如 Shift_JIS 或 UTF-16)儲存,Python 無法正確解碼就會發生錯誤。 對策: 在讀取檔案時明確指定編碼。with open('sample.xml', encoding='shift_jis') as f:
data = f.read()
import xml.etree.ElementTree as ET
root = ET.fromstring(data)或者,直接以二進位模式傳遞給 ElementTree.parse() 的方法也有效。ParseError:XML 語法不正確
錯誤內容:xml.etree.ElementTree.ParseError: not well-formed (invalid token): line 3, column 15原因: 如果 XML 檔案中的標籤未關閉、特殊字元(例如:&)未轉義、存在語法錯誤等情況,就會發生。 對策:- 從錯誤訊息中特定對應的行號和欄位
- 使用編輯器顯示 XML 的格式化,確認語法錯誤
- 進行特殊字元(例如:
&→&)的轉換
<note>
<text>5 & 3</text>
</note>修正後:<note>
<text>5 & 3</text>
</note>NoneType Attribute Error:存取不存在的元素
錯誤內容:AttributeError: 'NoneType' object has no attribute 'text'原因: 如果指定的標籤在 XML 中不存在,find() 會返回 None,直接存取 .text 就會發生錯誤。 對策: 在取得值之前檢查元素是否存在。title_elem = book.find('title')
if title_elem is not None:
title = title_elem.text
else:
title = '標題不明'或者,如果是 Python 3.8 以上,可以使用 Walrus 運算子(:=)來簡潔地撰寫。if (title_elem := book.find('title')) is not None:
print(title_elem.text)XML 檔案損壞或為空的情況
症狀:- 雖然沒有錯誤,但
getroot()返回None findall()無法取得任何東西
- XML 檔案為空(0 位元組)
- 資料中途截斷(下載失敗等)
- 確認檔案大小和內容
- 使用 XML 驗證工具進行語法檢查
- 檢討下載來源或產生來源的處理
Ad
6. 常見問題(FAQ)
本節中,我們將以問答形式,清楚解說那些想「用 Python 讀取 XML」的讀者常提出的疑問與不安。內容著重於事先解決實務或學習中容易卡住的要點。Q1. XML 檔案的編碼(字元碼)該如何處理?
A. XML 檔案通常在最上方有以下類似的編碼指定:<?xml version="1.0" encoding="UTF-8"?>Python 的 ElementTree 或 lxml 會自動讀取此宣告,但 開啟檔案時若字元碼不一致,就會發生錯誤。 日文的 XML 檔案有時會使用 Shift_JIS 或 EUC-JP,因此以下方式明確指定編碼較為安全:with open('sample.xml', encoding='shift_jis') as f:
data = f.read()此外,使用 lxml 可以更靈活地處理編碼。Q2. 處理大型 XML 檔案時會發生記憶體不足該怎麼辦?
A. 一舉讀取大型 XML 會將全部展開到記憶體,因此處理會變慢或發生錯誤的可能性。 這種情況下,使用能逐次讀取的「迭代器型解析器」會很有效。import xml.etree.ElementTree as ET
for event, elem in ET.iterparse('large.xml', events=('end',)):
if elem.tag == 'book':
print(elem.find('title').text)
elem.clear() # 記憶體解放這種方法可以只依序處理必要部分,從而節省記憶體。Q3. 不使用 JSON,而是使用 XML 的優點是什麼?
A. 現在許多 API 採用 JSON,但 XML 也有其獨特的優勢。- 能嚴格定義階層結構(DTD/XSD 等)
- 可區分屬性和元素的使用
- 適合文件導向,適用於設定檔案或組態資訊
- 在企業或自治體的系統中,XML 仍是主流
Q4. lxml 和 ElementTree 應該使用哪一個?
A. 可依以下標準來區分使用:| 函式庫 | 適合的情境 |
|---|---|
| ElementTree | 小型至中型 XML,使用標準函式庫即足夠的情形 |
| lxml | 想使用 XPath、希望高速處理、處理大量資料的情形 |
ElementTree 開始,但 若想使用 XPath 等進行靈活擷取,或需要處理速度的情形下,lxml 會很強大。Ad
7. 總結
在本篇文章中,我們以初學者易懂且適合實務應用的方式,說明了「使用 Python 讀取 XML 的方法」。使用 Python 讀取 XML 意外地簡單
標準程式庫的xml.etree.ElementTree 就能在不需特別準備的情況下立即讀取 XML。記住基本的語法和方法(parse()、find()、findall() 等),就能輕鬆取得和處理資料。根據用途選擇適當的程式庫很重要
- 小規模・簡單的處理:
ElementTree - 細部的節點操作或格式化輸出:
minidom - 高速處理或 XPath 搜尋:
lxml




