我在 "超圖解 Python 程式設計入門" 這本書中找到一個 Python 第三方的套件 xmltodict, 可讀取 XML 文件並轉成 Python 字典, 在擷取 XML 文件資訊上非常方便, 比起 Python 內建 xml 套件的 ElementTree 模組要簡單多了. 關於 ElementTree 模組用法參考 :
首先用 pip install 安裝套件 :
D:\Python\test>pip install xmltodict
Collecting xmltodict
Downloading xmltodict-0.13.0-py2.py3-none-any.whl (10.0 kB)
Installing collected packages: xmltodict
Successfully installed xmltodict-0.13.0
>>> import xmltodict
>>> xmltodict.__version__
'0.13.0'
用 dir() 檢視套件內容 :
>>> dir(xmltodict)
['AttributesImpl', 'ParsingInterrupted', 'StringIO', 'XMLGenerator', '_DictSAXHandler', '__author__', '__builtins__', '__cached__', '__doc__', '__file__', '__license__', '__loader__', '__name__', '__package__', '__spec__', '__version__', '_basestring', '_dict', '_emit', '_process_namespace', '_unicode', 'expat', 'isgenerator', 'parse', 'platform', 'unparse']
使用下列自訂模組 members.py 來檢視其內容 :
# members.py
import inspect
def varname(x):
return [k for k,v in inspect.currentframe().f_back.f_locals.items() if v is x][0]
def list_members(parent_obj):
members=dir(parent_obj)
parent_obj_name=varname(parent_obj)
for mbr in members:
child_obj=eval(parent_obj_name + '.' + mbr)
if not mbr.startswith('_'):
print(mbr, type(child_obj))
將此函式存成 members.py 模組, 放在目前供作目錄下, 然後匯入其 list_members() 函式來檢視 xmltodict 模組 :
>>> from members import list_members
>>> list_members(xmltodict)
AttributesImpl <class 'type'>
ParsingInterrupted <class 'type'>
StringIO <class 'type'>
XMLGenerator <class 'type'>
expat <class 'module'>
isgenerator <class 'function'>
parse <class 'function'>
platform <class 'module'>
unparse <class 'function'>
我們會用到的函式只有兩個 :
- parse(file) : 讀取 .xml 檔轉成字典傳回
- unparse(dict) : 將字典轉成 XML 字串
以下仍以之前測試 xml 套件的顯示卡 XML 文件 display_card.xml 為例 :
<?xml version="1.0" encoding="UTF-8"?>
<顯示卡>
<型號>NVidia RTX3060
<GPU核心編號>GA106-300</GPU核心編號>
<CUDA核心數>3584</CUDA核心數>
<TensorCores>112</TensorCores>
<VRAM 單位="GB" DDR="DDR6">12G</VRAM>
<ResizableBAR支援>有</ResizableBAR支援>
</型號>
</顯示卡>
用 with open() 以 utf-8 編碼開啟 .xml 檔, 讀取檔案內容後傳給 xmltodict.parse(), 它會將 XML 文件剖析為一個字典傳回 :
>>> with open('display_card.xml', encoding='utf-8') as f:
data=xmltodict.parse(f.read())
>>> data
{'顯示卡': {'型號': {'GPU核心編號': 'GA106-300', 'CUDA核心數': '3584', 'TensorCores': '112', 'VRAM': {'@單位': 'GB', '@DDR': 'DDR6', '#text': '12G'}, 'ResizableBAR支援': '有', '#text': 'NVidia GTX3060'}}}
>>> type(data)
<class 'dict'>
這樣就用字典的存取方式很容易取得 XML 裡面的資料了, 例如 :
>>> data['顯示卡']
{'型號': {'GPU核心編號': 'GA106-300', 'CUDA核心數': '3584', 'TensorCores': '112', 'VRAM': {'@單位': 'GB', '@DDR': 'DDR6', '#text': '12G'}, 'ResizableBAR支援': '有', '#text': 'NVidia GTX3060'}}
>>> data['顯示卡']['型號']
{'GPU核心編號': 'GA106-300', 'CUDA核心數': '3584', 'TensorCores': '112', 'VRAM': {'@單位': 'GB', '@DDR': 'DDR6', '#text': '12G'}, 'ResizableBAR支援': '有', '#text': 'NVidia GTX3060'}
>>> data['顯示卡']['型號']['GPU核心編號']
'GA106-300'
可見無屬性的標籤會以其內容當作值; 有屬性的標籤, 其值為一個字典, 屬性的鍵會以 @ 開頭後面跟著屬性名稱, 而內容的鍵則固定為 #text.
也可以呼叫字典物件的方法 :
>>> data['顯示卡']['型號'].keys() # 傳回鍵串列
dict_keys(['GPU核心編號', 'CUDA核心數', 'TensorCores', 'VRAM', 'ResizableBAR支援', '#text'])
>>> data['顯示卡']['型號'].values() # 傳回值串列
dict_values(['GA106-300', '3584', '112', {'@單位': 'GB', '@DDR': 'DDR6', '#text': '12G'}, '有', 'NVidia GTX3060'])
>>> data['顯示卡']['型號'].items() # 傳回鍵值對串列
dict_items([('GPU核心編號', 'GA106-300'), ('CUDA核心數', '3584'), ('TensorCores', '112'), ('VRAM', {'@單位': 'GB', '@DDR': 'DDR6', '#text': '12G'}), ('ResizableBAR支援', '有'), ('#text', 'NVidia GTX3060')])
新增標籤可以呼叫字典的 update({key, value}) 方法或 setdefault(key, value) :
>>> data['顯示卡']['型號'].update({"重量": "996g"})
>>> data['顯示卡']['型號']
{'GPU核心編號': 'GA106-300', 'CUDA核心數': '3584', 'TensorCores': '112', 'VRAM': {'@單位': 'GB', '@DDR': 'DDR6', '#text': '12G'}, 'ResizableBAR支援': '有', '#text': 'NVidia GTX3060', '重量': '996g'}
如果是有屬性的標籤, 則字典的值也是一個字典, 屬性在名稱前冠 @ 為鍵; 內容則以 #text 為鍵 :
>>> data['顯示卡']['型號'].update({"功耗": {"@單位": "瓦", "#text": "170W"}})
>>> data['顯示卡']['型號']
{'GPU核心編號': 'GA106-300', 'CUDA核心數': '3584', 'TensorCores': '112', 'VRAM': {'@單位': 'GB', '@DDR': 'DDR6', '#text': '12G'}, 'ResizableBAR支援': '有', '#text': 'NVidia GTX3060', '重量': '996g', '功耗': {'@單位': '瓦', '#text': '170W'}}
下面使用 set_default(key, value) 來新增節點 :
>>> data['顯示卡']['型號'].setdefault('HDCP支援', "有")
'有'
>>> data['顯示卡']['型號']
{'GPU核心編號': 'GA106-300', 'CUDA核心數': '3584', 'TensorCores': '112', 'VRAM': {'@單位': 'GB', '@DDR': 'DDR6', '#text': '12G'}, 'ResizableBAR支援': '有', '#text': 'NVidia GTX3060', '重量': '996g', '功耗': {'@單位': '瓦', '#text': '170W'}, 'HDCP支援': '有'}
如果標籤有屬性, 則 value 就傳入一個字典, 屬性在名稱前冠 @ 為鍵; 標籤的內容則固定以 #text 為鍵 :
>>> data['顯示卡']['型號'].setdefault('核心時脈', {'@單位': 'MHz', '#text': '1777'})
{'@單位': 'MHz', '#text': '1777'}
>>> data['顯示卡']['型號']
{'GPU核心編號': 'GA106-300', 'CUDA核心數': '3584', 'TensorCores': '112', 'VRAM': {'@單位': 'GB', '@DDR': 'DDR6', '#text': '12G'}, 'ResizableBAR支援': '有', '#text': 'NVidia GTX3060', '重量': '996g', '功耗': {'@單位': '瓦', '#text': '170W'}, 'HDCP支援': '有', '核心時脈': {'@單位': 'MHz', '#text': '1777'}}
呼叫 unparse() 函式並傳入字典可將其轉回 XML 文件, 語法如下 :
xmltodict.unparse(dict, [encoding='utf-8', full_document=True, pretty=False])
參數說明如下 :
- encoding : 預設為 utf-8 編碼
- full_document : 輸出完整 XML 文件, 預設為 True
- pretty : 是否將輸出文件, 預設 False
檢視目前修改後的 data 字典 :
>>> data
{'顯示卡': {'型號': {'GPU核心編號': 'GA106-300', 'CUDA核心數': '3584', 'TensorCores': '112', 'VRAM': {'@單位': 'GB', '@DDR': 'DDR6', '#text': '12G'}, 'ResizableBAR支援': '有', '#text': 'NVidia GTX3060', '重量': '996g', '功耗': {'@單位': '瓦', '#text': '170W'}, 'HDCP支援': '有', '核心時脈': {'@單位': 'MHz', '#text': '1777'}}}}
為了讓輸出格式化, 匯入 pprint.pprint() 函式 :
>>> from pprint import pprint
>>> xml_str=xmltodict.unparse(data)
>>> type(xml_str)
<class 'str'>
>>> pprint(xml_str)
('<?xml version="1.0" encoding="utf-8"?>\n'
'<顯示卡><型號><GPU核心編號>GA106-300</GPU核心編號><CUDA核心數>3584</CUDA核心數><TensorCores>112</TensorCores><VRAM '
'單位="GB" '
'DDR="DDR6">12G</VRAM><ResizableBAR支援>有</ResizableBAR支援><重量>996g</重量><功耗 '
'單位="瓦">170W</功耗><HDCP支援>有</HDCP支援><核心時脈 單位="MHz">1777</核心時脈>NVidia '
'GTX3060</型號></顯示卡>')
傳入 pretty=True 會輸出格式化字串 :
>>> xml_str=xmltodict.unparse(data, pretty=True)
>>> pprint(xml_str)
('<?xml version="1.0" encoding="utf-8"?>\n'
'<顯示卡>\n'
'\t<型號>\n'
'\t\t<GPU核心編號>GA106-300</GPU核心編號>\n'
'\t\t<CUDA核心數>3584</CUDA核心數>\n'
'\t\t<TensorCores>112</TensorCores>\n'
'\t\t<VRAM 單位="GB" DDR="DDR6">12G</VRAM>\n'
'\t\t<ResizableBAR支援>有</ResizableBAR支援>\n'
'\t\t<重量>996g</重量>\n'
'\t\t<功耗 單位="瓦">170W</功耗>\n'
'\t\t<HDCP支援>有</HDCP支援>\n'
'\t\t<核心時脈 單位="MHz">1777</核心時脈>\n'
'NVidia GTX3060\t</型號>\n'
'</顯示卡>')
用 with open() 將轉換結果寫入檔案 :
>>> with open('display_card_5.xml', 'w', encoding='utf-8') as f:
f.write(xml_str)
332
開啟檔案 display_card_5.xml 內容如下 :
<?xml version="1.0" encoding="utf-8"?>
<顯示卡>
<型號>
<GPU核心編號>GA106-300</GPU核心編號>
<CUDA核心數>3584</CUDA核心數>
<TensorCores>112</TensorCores>
<VRAM 單位="GB" DDR="DDR6">12G</VRAM>
<ResizableBAR支援>有</ResizableBAR支援>
<重量>996g</重量>
<功耗 單位="瓦">170W</功耗>
<HDCP支援>有</HDCP支援>
<核心時脈 單位="MHz">1777</核心時脈>
NVidia GTX3060 </型號>
</顯示卡>
這樣就把字典轉換成 XML 文件了, 比用 xml 的 ElementTree 模組要簡單許多.
沒有留言:
張貼留言