Pythonでxmlを取り扱う際には「lxml」を使うのが便利です.ただ使い方になれるまでは何かと引っかかる事も多くやっかいなので,ここで簡単にまとめておこうと思います.

xmlを読み込む

まず,xmlを読み込むには

from lxml import etree
root = etree.XML(string) #stringは文字列

 によりstringからXMLElementクラスのインスタンスを生成します.読み込みの際にはエンコードに気をつける必要がありますが,それについては後述します.またこの時,読みこんだ文字列stringがxmlとして崩れていると例外が吐かれます.特にlxmlはマークアップ以外の特殊文字コードの読み込みに対応していないので注意が必要です(>等は読み込めますがマークアップ以外の€等はҼ等の実体参照で記す必要があります).

なおxmlとして崩れていても,

>>> parser = etree.XMLParser(recover=True)
>>> root = etree.fromstring("<root> test &euro; lxml</root>", parser)
>>> etree.tostring(root)
<root> test lxml</root>

となり,lxmlで読み込めない部分をとばして読み込むことが可能です.rootが作成できればあとは簡単です.例えばあるタグ(要素)をまとめて読み込むには,

links = root.xpath('//link')
for link in links:
print link.text

とします.xmlからまとめて情報収集したいのならばXpathが非常に有用です.上の例では「//link : // = root以下の入れ子上の,link = link要素全て」という解釈になります.xpathメソッドにより見つかった要素のXMLElementクラスのリストが取得できます.

>>> links[1].text #要素の中のテキスト取得
’DrunkBoarder’
>>> links[1].attrib #要素が持つ属性のディクショナリを取得
{'uri' : "http://www.drunk-boarder.com", 'alt' : "drunk"}
>>> links[1].get('alt') #属性の値("alt")取得
'drunk'
>>> links[1].attrib['alt']
'drunk'
>>> links[1].tag #要素名の取得
'links'

読みこんだxmlの階層移動も簡単に行う事ができます.要素名の取得はtagにて行います.

>>> for children in root: # rootの子階層を取得
...    print children.tag
child1
child2
>>> child = root[0] # rootの子を取得
>>> print child.tag
child1
>>> r = child.getparent() # childの親を取得
>>> print r.tag
root

xmlを作成・改変する

xmlを新しく作成するには以下のように書きます.

>>> root = etree.Element("root")
>>> print root.tag
root

作成したxmlに子要素を追加するには,appendかaddnext, addpreviousもしくはSubElementをもちいます.

>>> #要素を子に追加する場合はappendメソッド
>>> root.append ( etree.Element("child1"))
>>> #もしくはSubElementを用いる
>>> child2 = etree.SubElement(root, "child2")
>>> print etree.tostring(root, pretty_print=True)
<root>
   <child1/>
   <child2/>
</root>
>>> #要素をある要素の後ろに追加するにはaddnextメソッド
>>> p = etree.Element('p')
>>> child2.addnext(p)
>>> print etree.tostring(child2.getparent(), pretty_print=True)
<root>
    <child1/>
    <child2/>
    <p/>
</root>
>>> #要素を一気に追加したいときはextendメソッドを使う
>>> p1 = etree.Element('p1')
>>> p2 = etree.Element('p2')
>>> child2.extend([p1,p2])
>>> print etree.tostring(root, pretty_print=True)
<root>
    <child1>
        <p1/>
        <p2/>
    </child1>
    <child2/>
    <p/>
</root>

tostringを用いる事でXMLElementの中身を吐き出す事が可能です.その際pretty_printをTrueに設定することで,整形したxmlを書き出す事が可能です.

>>> div = etree.XML("<div/>")
>>> part = etree.Element("part")
>>> chap = etree.SubElement(part, "chap")
>>> etree.tostring(chap)
<part><chap/></part>
>>> div.append(part) #divの子要素としてpartを追加
>>> etree.tostring(div)
<div><part><chap/></part></div>
>>> div = etree.XML("<div/>")
>>> div.extend([part]) #divの子要素としてpartを追加([]に注意)
>>> etree.tostring(div)
<div><part><chap/></part></div>
>>> div = etree.Element("div")
>>> div.extend(part) #divの子要素としてpartの子要素を追加
>>> etree.tostring(div)
<div><chap/></div>
>>> etree.tostring(part) #partの子要素は削除される
<part/>

テキストの追加・変更にはtextかtailを用いる事が可能です.また属性の追加,変更にはsetメソッドを,要素名の変更はtagを用います.

>>> div.text='start' #テキストを要素中に挿入する
>>> print etree.tostring(div)
<div>start<chap/></div>
>>> div.text='end' #テキストを指定した要素の後ろに挿入する
>>> chap.tail = 'end'
>>> print etree.tostring(div)
<div>test<chap/>end</div>
>>> chap.set('title', 'chapter') #属性の追加,変更にはset
>>> print etree.tostring(div)
<div>test<chap title="chapter"/>end</div>
>>> chap.set('title', 'sample')
>>> print etree.tostring(div)
<div>test<chap title="sample"/>end</div>
>>> chap.tag = "chapter" #要素名の変更はtag
>>> print etree.tostring(div)
<div>test<chapter title="sample"/>end</div>

また,上で紹介したXpathを用いる事でまとめて要素や属性を変更する事も可能です.

>>> #title="sample"となっているchapterタグを取得
>>> chap_sample_list = div.xpath('/chapter[@title="sample"]')
>>> #setメソッドで上書き
>>> for chap_sample in chap_sample_list:
...    chap_sample.set('title', 'test')
>>> print etree.tostring(div)
<div>test<chapter title="test"/>end</div>

消去はclearメソッドやdel文を使います.clearメソッドはその要素以下を消去します.textを消したい場合はtextに空文字を代入すれば消去できます.

>>> div.text = ""
>>> print etree.tostring(div)
<div><chapter title="sample"/>end</div>
>>> del chapter.attrib['title']
>>> print etree.tostring(div)
<div><chapter/>end</div>
>>> chap.clear()
>>> print etree.tostring(div)
<div><chapter/></div>

だいたい以上が使えるようになれば,不自由無くxmlを操作できるかと思います.最後にlxmlを用いる際に(限らずですが)気をつけなければいけないのが文字コードです.基本的には読み込みの際にdecodeされたユニコード文字列を,tostringの際に,encoding=’文字コード’を指定すれば問題ありません.この辺りのまとめはまた次の機会に…

Share Button

pythonでxml処理(lxml使い方まとめ)

<2013/01/18>