scrapiを使ってみた
馬術部のホームページをかってに弄ってみた
require 'rubygems' require 'pp' require 'scrapi' require 'open-uri' #$KCODE = 'u' # scrapeで警告が出るのでコメントアウト require 'kconv' # horse pages horses = Scraper.define do array :links process "td.hpb-lb-tb1-cell3 a", :links => "@href" result :links end horses_html = "http://circle.cc.hokudai.ac.jp/horse/homepage/horses.html" html = URI.parse(horses_html) horse_pages = horses.scrape(html, :parser_option => {:char_encoding => 'sjis'}) horse_pages.collect!{|l| horses_html.sub(/\/horses.html$/, "/#{l}")}
まず一覧ページからリンクを抽出したので、このリンク先のページから情報を取り出したい。
# horse page scrape scraper = Scraper.define do array :images process "td.hpb-lb-tb1-cell4 img", :name => "@alt" process "div>table td:nth-child(2)>table table>tbody>tr:nth-child(2)>td>table img", :images => "@src" result :name, :images end scraper.parser_options :show_warnings => true, :char_encoding => 'shiftjis' html = URI.parse horse_pages[0] horse = scraper.scrape(html) info = {} info[:name] = horse.name.toutf8 info[:images] = horse.images.collect{|s| horses_html.sub(/\/horses.html$/, "/#{s}")}
scrapiはcssセレクタで要素を指定できるので、細かな設定が可能。テーブルレイアウトでも目的の要素にclassやidが設定されていれば、簡単に指定できることもありそう。
それでも、構造のないHTMLをscrapingするのは難しい場合が多くて、この馬術部のHTMLの場合は、テーブル構造になっていてほしい情報が、スペースと改行(BR)でフラットに記述されているため、とってきた情報をテキスト解析する必要がある。
さらに、フラットなテキストの中にテーブルを含むため(つまり<div>1:aaa<br>2:ccc<table>...</table></div>)、:hoge=>:textの様にすると、テーブルの中身まで取得してしまう。
ちなみにその場合は、次のようにすれば、とりあえずなんとか目的が達せられる。
scraper = Scraper.define do process "div" :all_text => :text process "div>table" :table_text => :text result :all_text, :table_text end s = scraper.scrape(html) div_text = s.all_text.sub( Regexp.new(s.table_text) )
さらに、なんと各馬の詳細ページごとにHTMLの構造が異なることが判明。
この場合は、各ページごとに抽出方法を個別に記述する必要がある。セレクタの書き方によっては、柔軟に対応できるかもしれないが、今回のHTMLの場合は難しそう。人手で手間ひまかけて作成されるHTMLは、スクレーピングにも手間ひまかかる。