Tag/lxml

XPathでnamespaceにハマった。

lxmlXPath 使ってる時に namespace でハマったのでメモ。 ありがちなところでハマった。 恥さらしの為にもメモりますです。

どんなところでハマったかと言うと、 例えばこんな感じの test.xml なXMLがありまして、

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://example.com/hoge/1.0">
    <child>
        <type>1</type>
        <name>hoge</name>
    </child>
    <child>
        <type>1</type>
        <name>hige</name>
    </child>
    <child>
        <type>0</type>
        <name>hage</name>
    </child>
</root>

これを lxml でパースしてやろうとした訳です。 で、何も考えずにこんな事したら中身が取れなかった。

>>> from lxml import etree
>>> xml = etree.parse(open('test.xml', 'r'), parser=etree.XMLParser())
>>> xml
<etree._ElementTree object at 0x1290940>    # ココは取れてる。
>>> root = xml.getroot()
>>> root
<Element {http://example.com/hoge/1.0}root at 1236c60>  # ココもOK。
>>> #   ココからが問題。
>>> root.xpath('./child')
[]  # 空っぽ?

仕事中に色々試してみるも、 結局 namespace の指定の仕方が解らず撃沈。 が、家に帰ってきてから本家のドキュメント見てみると、 あっさりやり方が書いてあった。 どうやら xpath の第2引数で、 namespace を指定出来るらしい。

>>> root.xpath('./n:child', namespaces={'n':'http://example.com/hoge/1.0'})
[<Element {http://example.com/hoge/1.0}child at 1292fc0>,
 <Element {http://example.com/hoge/1.0}child at 1292ea0>,
 <Element {http://example.com/hoge/1.0}child at 1297030>]
>>>
>>> root.xpath('./n:child/n:name/text()', namespaces={'n':'http://example.com/hoge/1.0'})
['hoge', 'hige', 'hage']

とりあえず、めでたく XPath での問い合わせが出来た :-)

それにしても恥ずかしいな。この凡ミス。 なんで本家のドキュメントを読もうと思わなかったんだろ? そこが本当に不思議。

Posted at: 
2007/12/25 23:46:20
4 Comments
1 TrackBack
Tags: 
lxml
Python
XPath
Trackback: 
http://humming.via-kitchen.com/2007/12/25/namespace-trap-on-xpath/trackback/

lxmlを試してみたよ。(その2)

前回 に続いて lxml を使って色々やってみたよ。

テスト用のファイルを書くのもメンドクサイので、 このサイトのカテゴリーリストで遊んでみる事にした。

>>> from httplib import HTTPConnection
>>> from lxml import etree

>>> #   とりあえずHTMLを取ってくる。
>>> conn = HTTPConnection('humming.via-kitchen.com')
>>> conn.request('GET', '/')

>>> #   レスポンスをパース。
>>> html = etree.parse(conn.getresponse(), parser=etree.HTMLParser()).getroot()

ここから lxml でサクサク遊んでみる。

>>> #   カテゴリーリストを取ってくる。
>>> html.xpath('//div[@id="subContent"]/ul[@class="cloudList"]')
[<Element ul at 2489f90>]
>>> ul = html.xpath('//div[@id="subContent"]/ul[@class="cloudList"]')[0]

>>> #   リストのリンクを取ってくる。
>>> ul.xpath('./li/a')
[<Element a at 2489fc0>, <Element a at 248f720>, ..., <Element a at 248fe70>]

>>> #   リンクのテキストを取ってくる。
>>> ul.xpath('./li/a/text()')
['ActionScript', 'AIR', ..., 'vim']

テキストを取りたい場合は text() 、 ならアトリビュートはどうやって取るんだろう?と疑問が。

で、色々調べたり試したりした結果、 @ATTRIBUTE で取れるらしいっす。

>>> #   hrefを取ってくる。
>>> ul.xpath('./li/a/@href')
['/weblog/tag/actionscript/', '/weblog/tag/air/', ..., '/weblog/tag/vim/']

>>> #   classを取ってくる。
>>> ul.xpath('./li/a/@class')
['rank9', 'rank2', ..., 'rank2', 'rank8']

これが分かると一気に使い勝手が良くなってきた! もうね、 lxml 素敵。ってか XPath 素敵。

Posted at: 
2007/11/14 03:00:24
0 Comments
0 TrackBacks
Tags: 
lxml
Python
XPath
Trackback: 
http://humming.via-kitchen.com/2007/11/14/tried-using-lxml-part2/trackback/

lxmlを試してみたよ。

遅ればせながら lxml を試してみたよ。コレ、かなり便利っすなぁ。

インストールは easy_install でするのが簡単で良いと思う。 yolk で検索してみると、2.0alphaとかも出てきたので、 今回はバージョンを指定してインストールしてみた。

$ sudo easy_install lxml==1.3.6

テスト用にこんな感じのXMLを用意して、 test.xml とかで保存。

<?xml version="1.0" encoding="UTF-8"?>
<items>
    <item>
        <id>1</id>
        <name>hoge</name>
        <type>word</type>
    </item>
    <item>
        <id>2</id>
        <name>hige</name>
        <type>word</type>
    </item>
    <item>
        <id>3</id>
        <name>hage</name>
        <type>image</type>
    </item>
</items>

lxml を使って遊んでみる。

>>> from lxml import etree
>>> xml = etree.parse(open('test.xml', 'r'), parser=etree.XMLParser())
>>> xml
<etree._ElementTree object at 0x7406c0>

>>> #   ルートノード(items)を取ってくる。
>>> items = xml.getroot()
>>> items
<Element items at 7417b0>

>>> #   子要素(item)を取ってくる。
>>> items.getchildren()
[<Element item at 7417e0>, <Element item at 7418a0>, <Element item at 741840>]

>>> #   XPathで子要素(item)を取ってくる。
>>> items.xpath('./item')
[<Element item at 7417e0>, <Element item at 7418a0>, <Element item at 741840>]

>>> #   XPathで孫要素(name)のテキストを取ってくる。
>>> items.xpath('./item/name/text()')
['hoge', 'hige', 'hage']

文字列から直接パースする場合は、 etree.fromstring なメソッドを使うと良いらしい。

>>> text = open('test.xml', 'r').read()
>>> items = etree.fromstring(text, parser=etree.XMLParser())
>>> items
<Element items at 7418a0>

文字列からのパースだと、 getroot しなくても直接ルートノードが返ってくる。

逆に、ノードを文字列にしたい場合だと、 etree.tostring なメソッドを使うと良いみたい。

>>> #   itemノードの1個目を文字列に。
>>> etree.tostring(items.xpath('./item')[0])
<item>
    <id>1</id>
    <name>hoge</name>
    <type>word</type>
</item>

>>> #   最後のitemノードに含まれるnameノードを文字列に。
>>> etree.tostring(items.xpath('./item[position()=last()]/name')[0])
<name>hage</name>

色々簡単に使えていい感じ。 XPath もちゃんと覚えないとなぁ。 HTMLを扱うときは、 parseretree.HTMLParser にしてあげれば良いよ。

Posted at: 
2007/11/11 19:48:07
2 Comments
1 TrackBack
Tags: 
lxml
Python
XPath
Trackback: 
http://humming.via-kitchen.com/2007/11/11/tried-using-lxml/trackback/

Categories

Archives