xml

【Python】XMLをxml.etree.ElementTreeで読み込む方法

Pythonでxml.etree.ElementTreeを使ってXMLを読み込む方法について解説します。

XMLデータの解析と利用

XMLファイル

XMLファイルとは、Extensible Markup Languageのことで記述されたテキストファイルのことで、拡張子は「.xml」です。

XMLは、構造化されたデータで各種情報を表現できるのでシステムの設定ファイルとして使用されたり、SOAP(Simple Object Access Protocol)というWebアプリケーションの通信プロトコルで使用されたりします。

例えば、以下のような形式のファイルがXMLファイルです。

<?xml version="1.0" encoding="UTF-8" ?>

<memberlist>
  <member rank="normal">
    <no>1</no>
    <name>A</name>
    <address>愛知県名古屋市XXXXXX</address>
    <phone>090-0000-1111</phone>
  </member>
  <member rank="premium">
    <no>2</no>
    <name>B</name>
    <address>東京都新宿区XXXXXX</address>
    <phone>090-1111-0000</phone>
  </member>
  <member rank="normal">
    <no>3</no>
    <name>C</name>
    <address>大阪府大阪市XXXXXX</address>
    <phone>080-0000-1111</phone>
  </member>
</memberlist>

イメージとしてはメンバーリスト情報で、<memberlist>の中に<member>という要素があり、<member>内にはタグとしてno(番号)、name(名前)、address(住所)、phone(電話番号)といった情報を持っています。memberタグにはrankという属性として持たせています。

本記事では、PythonでXMLを読み込み、解析する方法について紹介しますが、上記ファイルを例に紹介しようと思います。

PythonでXMLデータ解析

PythonでXMLデータを読み込んで使用する場合には、xmlモジュールのxml.etree.ElementTreeというシンプルで軽量なXMLプロセッサを使用することができます。

以降で、xml.etree.ElementTreeを用いてXMLを読み込む方法について紹介します。

公式ドキュメントのこちらにも「警告」と記載がありますが、XMLモジュール群は不正なデータや悪意をデータを持って作成されたデータに対しては安全ではありませんので、注意してください。

信頼できないデータをパースする必要がある場合は、defusedxmlパッケージの使用が推奨されています。

Note

xml.etree.ElementTreeについての公式ドキュメントの記載はこちらを参考にしてください。

xml.etree.ElementTreeを用いたXMLの読み込み

xml.etree.ElementTreeの使用方法

XMLファイルから直接読み込み解析する

XMLファイルからXMLを読み込むにはxml.etree.ElementTreeを使って以下のように実行します。

import xml.etree.ElementTree as ET

# XMLファイルを解析する
xml_tree = ET.parse("./sample.xml")

# ルートを取得
root = xml_tree.getroot()

# ルートノードの表示
print("===== root")
print(root)
print(root.tag)

# 子ノードを読み込む
for child1 in root:
    print("--- member infomation")
    print(f"{child1.tag}: {child1.attrib}")
    for child2 in child1:
        print(f"{child2.tag}: {child2.text}")
【実行結果】
===== root
<Element 'memberlist' at 0x00000226525DAC20>
memberlist
--- member infomation
member: {'rank': 'normal'}
no: 1
name: A
address: 愛知県名古屋市XXXXXX
phone: 090-xxxx-xxxx
--- member infomation
member: {'rank': 'premium'}
no: 2
name: B
address: 東京都新宿区XXXXXX
phone: 090-xxxx-xxxx
--- member infomation
member: {'rank': 'normal'}
no: 3
name: C
address: 大阪府大阪市XXXXXX
phone: 080-xxxx-xxxxx

【詳細解説】

まずは、xml.etree.ElementTreeモジュールのインポートをします。公式ドキュメントの例を参考にETとして取得しています。

import xml.etree.ElementTree as ET

XMLを解析し、ルートノードを取得しているのが以下の部分です。

# XMLファイルを解析する
xml_tree = ET.parse("./sample.xml")

# ルートを取得
root = xml_tree.getroot()

XMLの解析ではparse関数を使用し、対象のXMLファイルを指定します。その後、getrootメソッドによりルートを取得できます。

XMLの情報は、rootから階層的にforを使って以下のように読み込むことができます。

# ルートノードの表示
print("===== root")
print(root)
print(root.tag)

# 子ノードを読み込む
for child1 in root:
    print("--- member infomation")
    print(f"{child1.tag}: {child1.attrib}")
    for child2 in child1:
        print(f"{child2.tag}: {child2.text}")

上記では、child1にmemberタグの階層の情報が入ります。child2には、memberタグ内の階層であるnoタグ、nameタグ、addressタグ、phoneタグの情報が順に入ります。

XMLタグの情報にアクセスするには、.tagでタグ、.attribで属性、.textで内容を取得することができます。

XML文字列から解析する

上記の例では、XMLファイルをparse関数で直接読み込みましたが、XML文字列から解析して同様に処理することができます。

import xml.etree.ElementTree as ET

# ファイルを読み込む
with open("./sample.xml", "r", encoding="utf-8") as f:
    xml_text = f.read()

# XML文字列を解析し、ルートを取得
root = ET.fromstring(xml_text)

# ルートノードの表示
print("===== root")
print(root)
print(root.tag)

# 子ノードを読み込む
for child1 in root:
    print("--- member infomation")
    print(f"{child1.tag}: {child1.attrib}")
    for child2 in child1:
        print(f"{child2.tag}: {child2.text}")
【実行結果】
===== root
<Element 'memberlist' at 0x0000015E4B9CF270>
memberlist
--- member infomation
member: {'rank': 'normal'}
no: 1
name: A
address: 愛知県名古屋市XXXXXX
phone: 090-xxxx-xxxx
--- member infomation
member: {'rank': 'premium'}
no: 2
name: B
address: 東京都新宿区XXXXXX
phone: 090-xxxx-xxxx
--- member infomation
member: {'rank': 'normal'}
no: 3
name: C
address: 大阪府大阪市XXXXXX
phone: 080-xxxx-xxxxx

文字列からXMLを解析してルートを取得するには、以下の部分のようにfromstringに対象の文字列を渡します。

# XML文字列を解析し、ルートを取得
root = ET.fromstring(xml_text)

その他の使い方は、上記で紹介した方法と同じように使用できます。