XPathおもしろ

最近,RubyのWWW::Mechanizeで遊んでいます.
WWW::Mechanizeは内部的にHpricot(HTML文書スクレイパ)を使っており,WWW::Mechanize経由でHpricotにsearchメソッドなどを送れるわけですが,このときにXPath式を渡すことで,目的の部分のHTML要素を得ることができます.これがまた気分爽快.たとえばこんな感じに使えます::

#!/usr/bin/ruby
# Yahoo! Newsのヘッドラインを抜き出す
require 'rubygems'
require 'mechanize'

agent = WWW::Mechanize.new
page = agent.get('http://headlines.yahoo.co.jp/hl')
headline = page/'//h1[@class="yjXL"]/a'  # '/'はsearchの別名
puts "Yahoo! News headline"
puts headline.first.inner_html

気分的には,HTML文書に直接XPath式のクエリを投げつけると,一致する部分文書が返ってくる,という感じで使えます.信じられないほどシンプルに書ける! (でしょ?)

ひと昔前なら,このような処理をする場合には,自前で正規表現をちまちま書いてスクレイピングするしかありませんでした.しかし,Hpricotがある今では,もうそんなことをする必要はありません.XPath式を書いてsearchに渡すだけで結果を受け取れます.うーん,いいねー.

そんなわけでXPathがマイブームなわけですが,ここで問題になるのはXPath式をどのように作るか,です.最初は,スクレイピングしたいHTML文書をエディタで開き,目でHTML文書のツリー構造を追いかけながら抜き出したい箇所を見つけ,手でXPath式をちまちまと書いていましたが,これはとても時間と手間がかかる作業です.少なくともプログラマの三大美徳のうちのひとつ「怠惰」に背く行為です.

ということで,以下のFirefoxアドオンを入れました.

あとFirebugも併用しています.これらの使い勝手については後ほど書くかも.

XPath関連のアドオンについては,しばらく両者を併用して,どちらか使いやすい方に絞ろうかと考えています.いまのところXPatherの方が少し肌に合う感じ.

そのほか(後で書くかも)
  • Firebugコンソールの$x関数にXPath式を渡すと要素の一覧が得られる
    • XPatherの方がやや便利かな

XPathめも(兄弟関係を表す軸)

  • following-sibling::*
    • 自分のノードの後続する兄弟.ほんとは4番目の要素を指したいけど,3番目の要素にしか名前(classとかidとか)が付いてない! みたいなときに便利.
    • following-sibling::*[2]とやると,自分より(document order的に)2つ後の兄弟を指す.
  • preceding-sibling::*
    • following-siblingの逆.自分より(document order的に)若い順の兄弟.面白いのは,例えばpreceding-sibling::*[2]とかやると,「自分より2つ若い兄弟」を指す,というところ.述部(ブラケットの内側)を配列のインデクス風にとらえていると,動きが違うのでちょっと戸惑う.

Rubyめも

Mutex#synchronize

synchronizeの途中でnextを使うとイテレータブロックを抜けるが,breakと動作の違いがあるか気になったため検証したところ,どちらも同じ動作(単純にsynchronizeブロックを抜けて次の行に行く)となった.

ただし,プログラムの見た目的にはbreakの方が意味的にもしっくりくるので,使うならbreakのほうに統一したほうがいいかもしれない.

Enumerable#inject

とても便利なので最近多用している.(後で書くかも)

pp

ppの出力はpp.pretty_inspectで取れる(Kernel#pretty_inspect).ppの出力結果をファイルに書きたい時などに便利.