事象
:/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