Exhibiの内部的な話を書こう書こうと思って忘れてた。とりあえずMechanizeについて。
Mechanizeはスクレイピングを楽にしてくれるRubygemsです。ExhibiではMechanizeを使ったスクレイピングのRakeタスクを作成し、それを日次で実行することで、各美術館のサイトから展覧会情報を抽出しています。抽出した情報がDB内に存在していれば無視。存在しないのならDBに追加。こういうクローリングに関しては、ちょうど時同じくしてRubyのクロール入門本が去年出たんですけど未読です。技術的な話のみならず、人様のサイトへ機械的にアクセスする際のお作法的なことも載っているらしく、いつかは読みたいところ。
SBクリエイティブ
売り上げランキング: 8,851
Mechanizeの内部動作
Mechanizeは同じくスクレイピング用のGemであるnokogiriに依存しており、スクレイピング結果はNokogiriのオブジェクトとして扱われるみたいです。nokogiri使ったことないのでよくわかりませんが。例えばあるurlに対してgetリクエストをかけた際、そのレスポンスをpage
インスタンスとして受け取った場合、この中身は単なるHTMLではなく、スクレイピング向きにアレンジされてます。page.links
でページ内のリンク一覧が取得できるし、page.search(XPath)
でXPathを使った検索ができます。
まあだいたいスクレイピングでやることって、ページを取得してきて①その中のリンクをクリックする、②フォームを埋めて送信して次のページヘ進む、③ページ内の要素からデータを抽出する、というところだと思うので、これぐらい押さえておけばなんとかなります。ちなみにurl与えてページを持ってくるときはMechanize
のインスタンス(公式チュートリアル内ではagent
という変数が振られてます)に対してagent.get(url)
というメソッド使えば返ってきます。簡単ですね。
ページ内の要素からデータを抽出する
自分はXPathをよく使います。
page.search('//td[@class="hoge"]').text
タグでももちろんいけます。
page.search('h3')[0].text
こちらだと戻り値が配列になる点に注意。XPathだと対象を一意に特定できるけど、タグだと同一ページ内に複数存在する可能性があるからなんだと思います。他にもやり方あるかもしれませんが、自分はこれしか使ってないです。あと、タグ内のテキスト部分を取り出したい場合は上記のやり方ですが、タグの要素の値を取り出すこともできます。
page.search('img')[0]["src"]
こんな感じで画像のURLを抽出したいときなどに使います。
リンクをクリックする
基本はa
タグで挟まれたテキストの値か、href属性の値から特定してクリックします。
page.link_with(text: 'hoge').click page.link_with(href: '/fuga').click
これでリンク先のpage
が返ります。が、クリックしたいのがテキストではない上に、href属性の値も特定できない場合もあります。Exhibiで必要な展覧会情報も基本的には個々の展覧会でパーマリンクが別なので、hrefから特定することはできないです。そういうときはXPathと上手いこと組み合わせます。
hrefs = page.search("//div[@class='hoge']/div/a/@href") links = Array.new hrefs.each do |href| links.push(page.link_with(href: href.text)) end links.each do |link| page = link.click ### なにか処理 ### end
またpage.links
ですべてのリンクが呼び出せるので、イテレータで全リンクに対して何か処理したりってこともできます。
フォームを埋めて送信する
Exhibiではフォームの処理は使っていませんが、一応書き留め。page
に対してform
メソッドを使うことで特定のフォームを取得し、値を埋めてsubmitできます。
f = page.form('LoginForm') f.name = "chroju" f.password = "password" page = f.submit
page#form
の引数に与えているのは、取得したいform
タグのname
要素です。以下、input
タグのname
要素がメソッドになっているので、ここに値を埋め込んでいき、最後に#submit
メソッドを呼び出します。
基本的にやることはこれだけなんですけど、ものによっては単純なフォームの送信ではなく、送信ボタンをクリックした時にonClick要素でJavaScriptを呼び出して処理させていたりします。そういう場合は単純にsubmitしてもバリデーションが通らない可能性があるので、呼び出している関数の中身を解析して対処するしかないです。大抵はhidden要素に対して何かしら値をいれこむような処理をしていることが多いので、その内容さえわかれば、JavaScript内で行われている値の代入処理を手で書いてあげれば無事にsubmitできたりします。
あとリンクと同じように、フォームもpage.forms
で全要素取得できます。
スクレイピング楽しい!!!
だいぶざっくりめに書いちゃいましたが、やっぱり公式ドキュメント読むのが一番いいとは思います。ここに書いている以外にもやれることはいろいろあるはずなので。
スクレイピング、API提供していないサイトであっても、すべてのネット上のデータを機械的に扱うことができるようになるので、結構夢が広がります。ただし、セマンティックウェブに則りきちんとしたHTMLを書いているサイトじゃないと上手いことコードが書けなかったりするので、ひとえに万能な魔法というわけではないのが悩ましかったりも。そういうときはウェブ上で簡単にスクレイピングが出来るらしいkimonoとか使うのもアリなのかもしれません。自分は使ったことはないですけど、kimonoの開発者が「セマンティックウェブは失敗した。ネット上のデータ構造化をウェブ製作者側ではなく、データの利用者側がやるべきだ」的なことを言っていて、これにはかなり共感を覚えたりしてます。