nokogiri でパース対象の項目が無かった場合に発生する nil:NilClass (NoMethodError) 対処法!

事象

:/Ruby200/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.7-x86-mingw32/lib/nokogiri/xml/node_set.rb:164:in `attr': undefined method `attribute' for nil:NilClass (NoMethodError)
    from C:/xampp/htdocs/worochi/Worochi/script/ruby/cr-amz-rankasin.rb:184:in `block (3 levels) in 
' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.7-x86-mingw32/lib/nokogiri/xml/node_set.rb:187:in `block in each' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.7-x86-mingw32/lib/nokogiri/xml/node_set.rb:186:in `upto' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.7-x86-mingw32/lib/nokogiri/xml/node_set.rb:186:in `each' from C:/xampp/htdocs/worochi/Worochi/script/ruby/cr-amz-rankasin.rb:181:in `block (2 levels) in
' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:229:in `call' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:229:in `block in do_page_blocks' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:228:in `each' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:228:in `do_page_blocks' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:167:in `block in run' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:163:in `loop' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:163:in `run' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:92:in `block in crawl' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:83:in `initialize' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:90:in `new' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:90:in `crawl' from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/core.rb:18:in `crawl' from C:/xampp/htdocs/worochi/Worochi/script/ruby/cr-amz-rankasin.rb:169:in `
'

原因

天下のAmazonサマといえども、完璧であることを前提としてはダメなのだな・・・これが原因だった。
タイトルにリンクがない。タイトルにリンクがあることを前提にしていたから甘かった。

考察

HTML や XML のパース処理全般に言えることなのかもしれないが、値があることを前提に処理していると値がなかった場合にエラーが発生する。
Perl の Web::Scraper だと死には至らなかったように思うが、Ruby の nokogiri では掲題のように nokogiri でパース対象の項目が無かった場合に発生する nil:NilClass (NoMethodError) で死ぬのでそれ以降の処理は行なわれずスクリプトは停止してしまうので対処が必要だ。

ググったら、Ruby では active_support という機能拡張?があり、require すると利用できるようになる便利なメソッドが存在することを知ったのだが、これが便利!
特に、nil? や try() が Web スクレイピングでは活用できそうだ。

nil? の例はゆとりプログラマー baba さんの記事で知った。

try の用法については SlideShare に公開されている株式会社クルウィットの井澤志充さんの資料がすぐ読めるし判りやすくてためになりました。で, try を利用したいのだがそもそも xpath で切り出す際にリンクがあるのか?を判定するために

item.xpath(“div[\”zg_title\”]/a”).try { # 値の取得処理 }
とか
item.xpath(“div[\”zg_title\”]/a”).try(:href)
とかやってみたのだけどうまくいかなかったので nil? を利用して nil だったらあらかじめ決め打ちの値を補完するようにした。

対処

nil? って聞いておいて true だったら @@@@@@@@@@ をASINとして登録w あ、wwwwwwwwwwでもよかったかもwww 草植系男子www とまらないwww

変更前

Anemone.crawl(urls, opts) do |anemone|
  anemone.on_every_page do |page|

  #文字コードをUTF8に変換したうえで、Nokogiriでパース
  doc = Nokogiri::HTML.parse(page.body.toutf8)

  #カテゴリ名の表示
  category = doc.xpath("//*[@id='zg_browseRoot']/ul/li/a").text
  sub_category = doc.xpath("//*[@id=\"zg_listTitle\"]/span").text

  items = doc.xpath("//div[@class=\"zg_itemRow\"]/div[1]/div[2]")
  items += doc.xpath("//div[@class=\"zg_itemRow\"]/div[2]/div[2]")
  items.each{|item|
    # ASIN
    asin = item.xpath("div[\"zg_title\"]/a")
      .attribute("href").text.match(%r{dp/(.+?)/})[1]
  }
  end
end

変更後

Anemone.crawl(urls, opts) do |anemone|
  anemone.on_every_page do |page|

  #文字コードをUTF8に変換したうえで、Nokogiriでパース
  doc = Nokogiri::HTML.parse(page.body.toutf8)

  #カテゴリ名の表示
  category = doc.xpath("//*[@id='zg_browseRoot']/ul/li/a").text
  sub_category = doc.xpath("//*[@id=\"zg_listTitle\"]/span").text

  items = doc.xpath("//div[@class=\"zg_itemRow\"]/div[1]/div[2]")
  items += doc.xpath("//div[@class=\"zg_itemRow\"]/div[2]/div[2]")
  items.each{|item|
    # ASIN
    item.xpath("div[\"zg_title\"]/a").nil? {
      asin = '@@@@@@@@@@'
    }
    asin = item.xpath("div[\"zg_title\"]/a").attribute("href").text.match(%r{dp/(.+?)/})[1]
  end
end
スポンサーリンク