RubyでAmazon API以上の詳細検索をしたい( capybara, poltergeist ) 


クローラーを作っている最中に他のサイトの商品名のASINやAmazonでの新品の最安値などが取得したいと思ったので、Rubyのamazon-ecsでitem-searchしてみた。

# amazon-ecsで商品検索
```ruby:
require 'amazon/ecs'
res = Amazon::Ecs.item_search("Ruby", response_group: "OfferSummary", country: "jp")
res.first_item.get("OfferSummary/LowestUsedPrice/FormattedPrice")
  #=> "¥ ????"
```
amazon/ecsを用いるとすごい簡単にAmazonの商品検索ができてそのTOP商品のASINや価格、著者が取得できる。個人的にはここが分かりやすかった -> [amazon-ecs gem メモ](http://d.hatena.ne.jp/riocampos+tech/20150306/amazon_ecs_gem)

ただこのAmazon APIは詳細な検索には対応していないようで、ヨドバシさんからたまたま見つけた商品名

**TOTO トートー
YEW350 [携帯型ウォシュレット]**

そのままサイトからコピペしただけなので変な場所に改行が入っている。ただ、これをAmazonのサイトにそのままの形でコピペしても
![スクリーンショット 2015-08-29 5.01.12.png](https://qiita-image-store.s3.amazonaws.com/0/85665/ac9ccdff-2dd9-f02e-7e3b-24405bbfb941.png)
このようにしっかりと検索結果を表示してくれるが、上のgemを用いると

```ruby:
require 'amazon/ecs'
res = Amazon::Ecs.item_search("TOTO トートーYEW350 [携帯型ウォシュレット]", response_group: "OfferSummary", country: "jp")
res.total_results
  #=> 0
res.first_item.get("OfferSummary/LowestUsedPrice/FormattedPrice")
  #=> undefined method `get' for nil:NilClass (NoMethodError)
```
といったように、同じ検索内容なのに検索結果なんてねぇよ！って怒られてしまいました。
なので、実際にAmazonの検索サイトに商品名を打ち込んで結果が表示されたような動作をcapybaraで書いて実行させてみることにします。一気にソース全文ですが、こんな感じに
# capybara/poltergeistで商品検索
```ruby:amazon.rb
require 'capybara'
require 'capybara/dsl'
require 'capybara/poltergeist'

# Capybaraの設定
Capybara.configure do |config|
	config.run_server = false
	config.current_driver = :poltergeist
	config.javascript_driver = :poltergeist
	config.default_wait_time = 5
	config.default_selector = :xpath
end

Capybara.register_driver :poltergeist do |app|
  Capybara::Poltergeist::Driver.new(app, {
                    js_errors: false,
                    timeout: 1000,
                    phantomjs_options: [
                              '--load-images=no',
                              '--ignore-ssl-errors=yes',
                              '--ssl-protocol=any']})
end

module Crawler
	class Amazon
		include Capybara::DSL
		def initialize(pName)
			@pName = pName
		end

		#商品名pNameを引数にAmazonで検索してTopの検索結果のページに遷移
		def lookup pName=@pName
			page.driver.headers = { "User-Agent" => "Mac Chrome" }
			visit URI.escape("http://www.amazon.co.jp/s/ref=nb_sb_noss?__mk_ja_JP=カタカナ&url=search-alias%3Daps&field-keywords=" + pName)
			# 検索結果が一つもなかった場合
			if page.has_xpath?("//*[@id='noResultsTitle']") 
				puts "検索結果一つもなし"
				return
			end
            # 検索TOP商品に遷移
			visit find(:xpath, "//*[@id='result_0']/div/div[2]/div[1]/a")[:href]
		end

		#商品ページに来たので、そのASINと最安値を取得する。
		def getASIN
			puts "#{@pName}のASIN取得中..."
			if first('//*[@id="prodDetails"]/div/div[2]/div[1]/div[2]/div/div/table/tbody/tr[1]/td[2]')
				asin = first('//*[@id="prodDetails"]/div/div[2]/div[1]/div[2]/div/div/table').text
			else
				asin = first("//*[@id='detail_bullets_id']/table/tbody/tr/td/div[@class='content']").text
			end
			if asin =~ /([A-Z0-9]{10})/
				asin = $1
			end

		# 新品の最安値を取得 新品の最安値がなければ正規の価格。それがなければ"0"
			lowest_price = first("//*[@id='olp_feature_div']/div/span/span")? 
				first("//*[@id='olp_feature_div']/div/span/span").text : 
					(first('//*[@id="priceblock_ourprice"]')? first('//*[@id="priceblock_ourprice"]').text : "0")
			lowest_price.gsub!(/[^\d]/, "").to_i

			puts asin
			puts lowest_price
		end


		# javascriptロードのための待機時間
		def wait_for_ajax
			Timeout.timeout(Capybara.default_wait_time) do
				loop until page.evaluate_script('jQuery.active').zero?
			end
		end
	end
end
crawler = Crawler::Amazon.new("TOTO トートーYEW350 [携帯型ウォシュレット]")
crawler.lookup  # 商品名でAmazon検索したTOP商品のページに遷移
crawler.getASIN # その遷移先のページにあるASINや価格を取得
```
最初のlookup関数から

```ruby
visit URI.escape("http://www.amazon.co.jp/s/ref=nb_sb_noss?__mk_ja_JP=カタカナ&url=search-alias%3Daps&field-keywords=" + pName)
```
としていて人間が検索するように検索している感一切ないですが、最初の商品検索の部分はfield-keywordsの値が変わっているだけだったのでこれで検索しているように見せれました。もっとも人が検索するように検索している感を出したいのならこうしてもおそらくいいと思います

```rb
fill_in "field_keywords", :with => "ここに検索する文字列"
find(:xpath, '//*[@id="nav-search"]/form/div[2]/div/input').click
```

こうすればnameがfield-keywordsのinput部分に"ここに検索する文字列"を人が入力してとなりの検索するボタンはnameがなかったのでXpathで指定してあげてクリックする。

こうして遷移した先の商品のASINを取得する部分ですが、商品によってASINが描かれている部分がかなり違ったのでソースコードの中がよくわかんないくらいぐちゃぐちゃになってしまいました。。何かいいスクレイピンフの仕方見つけた方いたら教えてください！！
ピンポイントにXpathを指定しても記載情報の量によってli[3]かli[4]といったように場所にわずかなズレがあったので、大雑把にその部分のテキスト全部取り出して、ASINが10桁の英数字だからってことで、マッチする部分を抜き出していますが、万が一の例外処理とかも書いていないですが、よければ使ってみてください。以上、初の投稿でビクビクしている者でした。