ライブラリ: ElementTree

Pythonの「ElementTree」というライブラリについてご紹介します。

from xml.etree.ElementTree import *

PythonにはXMLを扱うためのメジャーなライブラリがいくつか存在します。シェアがどのようになっているかはよくわからないのですが、個人的によく見かけるのは、ElementTree、BeautifulSoup、lxmlあたりでしょうか。minidomもときどき目にします。

今回はそのうちの「ElementTree」について見てみたいと思います。

「ElementTree」は、XMLをファイルやテキストから読み込んだり、加工したり、保存したりするためのライブラリです。Python 2.5から標準ライブラリとなり、別途インストールをすることなく最初から使うことができます。

ファイルからの読み込み

from xml.etree import ElementTree

XMLFILE = "sample.xml"

tree = ElementTree.parse(XMLFILE)  # ファイルから読み込み
root = tree.getroot()
print root.tag  # feedと出力

ファイルからの読み込みにはparse()を使います。続いてgetroot()でルートとなるノードを取得しから処理を進める、というのが定番の流れとなっています。

以下、sample.xmlの中身が次のようなものだと仮定して見ていきます。

<feed attrA="value A" attrB="value B">
    <title>title C</title>
    <categories>
        <category term="Asia" />
        <category term="South America" />
        <category term="Europe" />
    </categories>
</feed>

要素情報の検索と取得 XML要素のタグ、属性はtag、attribアトリビュートに格納されています。

print root.tag  # feed
print root.attrib  # {'attrB': 'value B', 'attrA': 'value A'}

attribは、属性と値のセットを辞書として格納しています。

要素の子要素を見るには、次のようにします。

for e in root:
    print e.tag  # title, categoriesと出力

子要素の中から特定の子だけを取り出すには、find()を使います。

e = root.find('title')
print e.tag  # titleと出力
print e.text  # title Cと出力

textアトリビュートには、その要素の中にあるテキストが格納されています。

find()は上から走査して、最初に見つかった要素1つだけを返します。すべての要素を取得する場合はfindall()を使います。

es = root.findall('category')
for e in es:
    print e.tag, e.attrib  # 出力なし

ただこの場合は、categoryはfeedの直接の子ではなく孫要素にあたるため、findall('category')では取得することができません。直接の子要素だけでなく、すべての子孫要素の中からすべて取得するにはfindall()の引数の頭に「.//」を追加します。

es = root.findall('.//category')
for e in es:
    print e.tag, e.attrib
# 以下のように出力
# category {'term': 'Asia'}
# category {'term': 'South America'}
# category {'term': 'Europe'}

さらに、属性値も手がかりにして要素を検索するには次のようにします。

es = root.findall(".//category[@term='Asia']")
for e in es:
    print e.tag, e.attrib
# category {'term': 'Asia'}と出力

属性名の前に@をつけ、属性値と=でつなげて使います。クオートは必ず「'」(シングルクオート)を使います。「'」が「"」になっていたり、間に空白が挟まっていたりするとうまく検索できないので注意が必要です。

この他にも、find()・findall()には「指定した要素の子要素だけ」、「指定した要素を子に持った親要素だけ」を抽出するような機能なんかも備わっています。find()・findall()の文法については、公式サイトのXPathシンタックスに関するパートにまとめられていますので詳しくはそちらをご参照ください。

次に、文字列から起こすやり方を見てみます。

文字列からの読み込み

xml = """

<feed attr="attr A">
<text>text B</text>
<category term="Asia">
<category term="South America">
<category term="Europe">
</category></category></category></feed>

"""

e = ElementTree.fromstring(xml)  # 文字列からの読み込み

文字列からElementを生成するには、fromstring()を使います。fromstring()で作った変数は、ファイルから読み込んだときのrootと同じように使うことができます。

最後に、ファイルに保存するやり方を。

ファイルへの書き込み

XMLFILEOUT = 'sampleout.xml'

tree = ElementTree.ElementTree(e)
tree.write(XMLFILEOUT)  # ファイルへの書き込み

上で文字列から読み込んだeをファイルに保存するには、それを内包するElementTreeというものを生成した上で、write()メソッドで行います。

ファイルから開いたtreeの場合は直接write()で保存できます。

ちなみに、この「ファイルから読み込んだ場合と文字列から読み込んだ場合のちがい」は、ファイルからparse()で開いた場合はElementTreeオブジェクトのインスタンスが生成されるのに対し、文字列からfromstring()で開いた場合はElementオブジェクトのインスタンスが生成される、というちがいに起因しています。詳しくは公式ドキュメントをあたっていただくのがよいかと思います。

以上です。

ElementTreeにはこれらの他にも、文字列を検索するfindtext()、属性を追加するset()、子要素を追加するinsert()など、XMLを手軽に扱える便利な機能が数多く用意されています。

もっと詳しい部分については公式のドキュメントや、以下参考リンクをご参照ください。

インストール 「ElementTree」はPython 2.5から標準ライブラリに含まれています。Pythonと別途インストールする必要はありません。

参考