どうもてぃです。
とある巨大ECサイト(通称熱帯雨林)をスクレイピングして遊んでいます。
今回は nokogiri
で問題が発生したので記事にしました。
nokogiriのスクレイピング精度はあんまり良くないのを実体験で痛感しましたね。
environment
issue
ページネーション要素の取得です。
Nokogiriだと4〜6割の確率でページネーションを取得できない場合があります。
問題なのが、Seleniumだとページを読み込み終わるまで待つSelenium::WebDriver::Wait
があるのに対して、Nokogiriにはwaitが存在しません。
あと、NokogiriではJS等で動的に表示しているページは上手く取得できないことがあるとのこと。
だからSeleniumでブラウジングしてスクレイピングする方が成功確率は高いと。
最初からSelenium使えばよかったですわ。Python
もSeleniumだし。
solution
試しに、一番お手軽なスクレイピングサイト、巨大ECサイト熱帯雨林を使ってみます。
ここだと、ページネーションもありますので。
最近switchのスマブラが販売されて転売ヤーさんが乱立しているGCコンをスクレイピングしてみます。
require 'selenium-webdriver' ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36" # headlessモードのoptionをつける options = Selenium::WebDriver::Chrome::Options.new options.add_argument('--headless') options.add_argument('--disable-gpu') options.add_argument("--user-agent=#{ua}") driver = Selenium::WebDriver.for(:chrome, options: options) driver.get('https://www.amazon.co.jp/gp/offer-listing/B07HC2F97Q/ref=dp_olp_new?ie=UTF8&condition=new') # ページ読み込みのwaitを設定 wait = Selenium::WebDriver::Wait.new(timeout: 10) # find_elementで単数取得 wait.until { driver.find_elements(:xpath, "//h3[@class='a-spacing-none olpSellerName']/span/a") } seller_info = driver.find_elements(:xpath, "//h3[@class='a-spacing-none olpSellerName']/span/a") wait.until { driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").map { |el| el.attribute(:href) } } pagination = driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").map { |el| el.attribute(:href) } # ページネーションの最後までクリックする loop.with_index(1) do |_, i| puts "==================================#{i}回目===================================" puts driver.find_elements(:xpath, "//h3[@class='a-spacing-none olpSellerName']").map(&:text) break if driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").last[-1] == '#' driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").last.click end # 二回目のget urlも問題ない driver.get('https://www.amazon.co.jp/sp?_encoding=UTF8&seller=A2W8UAZHGIMLV7') driver.close
上記ソースを超簡単に説明。
# headlessモードのoptionをつける options = Selenium::WebDriver::Chrome::Options.new options.add_argument('--headless') options.add_argument('--disable-gpu') options.add_argument("--user-agent=#{ua}")
Selenium::WebDriver::Chrome::Options.new
でSeleniumのオプションを設定するインスタンスを生成します。
AWS
やGCP
で動かすことも想定にいれて、ヘッダレスでブラウザを起動しないようなオプションをつけています。
ついでにユーザーエージェントも設定してます。
# ページ読み込みのwaitを設定 wait = Selenium::WebDriver::Wait.new(timeout: 10)
スクレイピングをした際にページが全て表示されるまで待つ必要が有ります。
その設定を上記でやっています。使い方は
wait.until { driver.find_elements(:xpath, "//h3[@class='a-spacing-none olpSellerName']/span/a") } seller_info = driver.find_elements(:xpath, "//h3[@class='a-spacing-none olpSellerName']/span/a") wait.until { driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").map { |el| el.attribute(:href) } } pagination = driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").map { |el| el.attribute(:href) }
のように、要素を取得する前にwait.util
を設定します。簡単です。
残りは要素の取得の処理がメインですね。
loopの部分でページネーションのリンクをクリックし続ける処理をやっています。そんな大したことはやってないです。
最後に
やはり、困った時はrubydocが役に立ちます。
Class: Selenium::WebDriver::Driver — Documentation for selenium-webdriver (0.0.28)
是非参考にしてみてください。
なにかあればコメント欄で〜。
ではでは。