にせねこメモ

はてなダイアリーがUTF-8じゃないので移ってきました。

Python 3のElementTreeでXMLを解析する

PythonのElementTreeを使ってXMLを解析するときに少し悩んだので、忘れないようにメモ。
もっと例を洗練させるべきだとは思うが、後回しにする。

インポートとファイルの読み込み

import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()

一方、ET.fromstring()メソッドで作成する場合、返されるのはルート要素となる。

タグを取り出す

以下、elemは何らかのタグに対応する要素とする(上のrootなど)

再帰

.iter()メソッドは、深いものについても順番に見ていくイテレータを返す。

for target_tag in elem.iter("target"):
    #...

とか。

直下で最初にみつけたもの1つ

.find()メソッドは直下のもののみ、最初に一致した要素一つだけを返す。

target_tag = elem.find("target")

これは後述するXPath記法と組み合わせることで格段に便利になる。

マッチしない場合はNoneになる。ただし、

if target_tag: #この書き方では動かない
    #マッチした場合

みたいには書けない(マッチした場合でも実行されない)。なので、

if target_tag is not None:
    #マッチした場合

みたいに書いたら動いた。

直下でマッチするものすべて

.findall()メソッドは直下のもののみ、一致した要素をすべて含むリストを返す。

for target_tag in elem.find("target"):
    #...
添え字でのアクセス

内側のタグには添え字でもアクセスできる。直下で最初くるタグのそのまた直下の最初に来るタグだと次のように書ける。

elem[0][0]

XPath記法

これを使えると格段に解析効率が上がる

今の階層以下の(直下とは限らない)タグを検索

//という、子要素やその子孫の要素すべてを対象とする記法が使える。

target = elem.find(".//target")
階層構造を指定
<a><b><c>こんにちは</c></b></a>
element = ET.fromstring("<element><a><b><c>こんにちは</c></b></a></element>")
target_c = element.find("a/b/c")
属性を指定
target = elem.find("target[@attrib='value']")

属性が"'を含む場合、エスケープするなり引用符を変更するなどすれば動く。

element = ET.fromstring('''<root><target name="Do's and dont's">some text</target></root>''')
target = element.find('''target[@name="Do's and dont's"]''')
2つ以上の属性を指定
target = elem.find("target[@attrib1='value 1'][@attrib2='value 2']")

属性の取り出し

element = ET.fromstring('<greetings myvalue="hello world"/>')
element.get("myvalue") #"hello world"

テキスト取り出し

<a><b>いz<c></c></b><d></d></a>

みたいな構造があった場合、
a.textは「あ」になる

a = ET.fromstring("<a>あ<b>い<c>う</c></b><d>え</d>お</a>")
print(a.text) #"あ"

ここで、「あいうえお」が欲しければ、

print( "".join(a.itertext()) ) #あいうえお

のようにするとよい。.itertext()で現在の要素以下のテキストを順番に返すイテレータが得られるので、それを合体させている。