lxmlを使ってXMLを生成したり、パースしたりするという処理をたまに書く。そんなに頻繁にやる訳ではないので、処理の書き方を忘れてしまいがち。
備忘録として書いておく。なお、htmlは今回扱わないので、別のサイトを見てください。
目次
installとimport
$ pip install lxml
>>> from lxml import etree
文字列とetreeの相互変換
>>> hoge = etree.fromstring('<hoge hoge="hoge"> hoge </hoge>') >>> s = etree.tostring(hoge, pretty_print=True).decode() >>> print(s) # <hoge hoge="hoge"> hoge </hoge>
せめてtostringはメソッドとして叩ければ、という気がしなくもない。あと、str(hoge)とかしてもXML文字列に変換されたりはしない。ちょっと残念。
デフォルトでtostringがbyte列を吐いてくれるのは、python2系との互換性を重視しているのか、パフォーマンス上の理由なのか、どちらかなのだろう。
覚えておくべきことは「etree.tostringとetree.fromstringを使えば良く(インスタンスメソッドなどではない)、to_stringやfrom_stringではない(アンダースコアは入らない)」です。これでもう迷わない。
要素を作る
# 要素を作る >>> hoge = etree.Element("hoge") # textを追加 >>> hoge.text = "hoge" # attribを追加 >>> hoge.attrib["hoge"] = "hoge"
これで上の例の
tagを取得、変更
>>> hoge.tag "hoge" >>> hoge.tag = "fuga" # ちゃんとタグが変わる
tagがmutableなのはちょっと恐ろしい。
子要素を追加・削除
なぜかリスト風のインターフェイスになっている。
>>> hoge = etree.Element("hoge") >>> hoge.append(etree.Element("fuga")) # -> tostringすると<hoge><fuga/></hoge> >>> hoge.remove(hoge[0]) # -> tostringすると<hoge></hoge>
親、子を取得
>>> hoge = etree.fromstring('<hoge hoge="hoge"> hoge <fuga/></hoge>') >>> hoge.getchildren() # getchildrenは非推奨 [<Element fuga at 0x7f2ce13d0108>] >>> list(hoge) # こちらを使うべき [<Element fuga at 0x7f2ce13d0108>] >>> hoge[0].getparent() # これは特に非推奨とかではない <Element hoge at 0x7f2cdfecd948>
ぜんぶたどる
pre-orderで探索してくれる。
>>> hoge = etree.fromstring('<hoge hoge="hoge"> hoge <fuga/><piyo><piyohoge/></piyo></hoge>') >>> for x in hoge.iter(): ... print(x.tag) ... hoge fuga piyo piyohoge
- pre-orderの説明
上のXMLを木構造で書くとこんな感じ。
hoge /\ fuga piyo | piyohoge
hogeをスタート地点にし、指を置く。左側に指を動かし、そのまま木の外側をぐるっと辿ってスタートに戻る。
このとき指はhoge→fuga→hoge→piyo→piyohoge→hogeの順に当たる。二回も三回も同じものを見る必要はないので、最初に見たとき記録し(然るべき処理を行い)、後は同じものに当たってもスキップすることにすると、hoge→fuga→piyo→piyohogeの順番になる。
XPATHで検索
>>> hoge = etree.fromstring('<hoge hoge="hoge"> hoge <fuga/><piyo><piyohoge/></piyo></hoge>') >>> hoge.xpath("//piyohoge")
マッチするものが全部返るので結果はリスト。中身はエレメントのオブジェクト。
XPATH自体についての解説はしない。いずれ気が向いたら書くかもしれないが、現時点では他のサイトをあたってください。