Rubyでのスクレイピングを考えた時に、WEBサイトにJavascriptが使われていた場合Selenium一択になると思います。
そんなSeleniumの使い方をインストールから実行までまとめてみました。
目次
1、seleniumライブラリ
・よく使うやり方 例
2、ヘッドレスオプション(ブラウザ表示をなくす)
3、スクリーンショットの撮り方
4、画像を保存
5、ページ遷移
6、find_elementでNoSuchElementErrorが発生したとき
7、iflame
8、seleniumでスクロール
9、seleniumでページネーション
10、新規タブの作成
1.seleniumのインストール
まずはgemのインストール
$ gem install selenium-webdriver
以前はgemのselenium-webdriverを使うにはchromdriverをインストールしないといけませんでした。
"selenium-webdriver"のバージョン4.0.0以降は"selenium-manager"という新機能が追加されたので自動的にPATHを指定してくれるのでダウンロードもしなくてもよくりすごく便利になりました。
基本の使い方
require "selenium-webdriver"
driver = Selenium::WebDriver.for :chrome
# googleのページを立ち上げる
driver.get("http://google.com")
#googleの検索フォームに"example.com"と文字を打ち込み検索をかける
element = driver.find_element(name: "q")
element.send_key("example.com")
element.submit
# 5秒間停止
sleep 5
# ブラウザを閉じる
driver.quit
* googleで"example.com"の文字を検索して閉じるプログラム
よく使う要素を簡単に取得する方法
require "selenium-webdriver"
driver = Selenium::WebDriver.for :chrome
driver.navigate.to "http://google.com"
#デベロッパーツールを開いて要素を右クリック→copy→xpathをcopy「//*[@id="rso"]/div[1]/div/div/div/div/div/div[1]/a/h3」
driver.find_element(:xpath,'//*[@id="rso"]/div[1]/div/div/div/div/div/div[1]/a/h3').click
find_elementを繋げてattribute("href")でURLを取得する場合
require "selenium-webdriver"
driver = Selenium::WebDriver.for :chrome
nashi = driver.get("https://nashiblog.netlify.app/")
element = driver.find_elements(:tag_name,"a")[2]
puts element.attribute("href")
結果=> https://nashiblog.netlify.app/blog/javascript/rubymatch
eachでループをまわしてa属性のテキストを配列で取得
require "selenium-webdriver"
d = Selenium::WebDriver.for :chrome
text = []
d.get("https://nashiblog.netlify.app/")
d.find_elements(:tag_name,"a").each do |u|
text << u.text
end
p text
sleep 3
結果=> ["Nashi Blog", "プログラミング", "ruby 正規表現 【まとめ】", "ruby スクレイピング テクニック", "ruby 競技用プログラミング基本 【初心者用】", "ruby 要素数の取得", "ruby 配列の小技", "gitの基本 初心者用", "rails つまづきポイント", "heroku デプロイまで", "rails javascriptの読み込み方", "rails bootstrap導入", "プログラミングブログ一覧"]
2.スクレイピングの際にブラウザ表示をなくす場合(ヘッドレスオプションの設定)
options = Selenium::WebDriver::Chrome::Options.new
options.headless! #ヘッドレスオプションを設定(ブラウザ表示をなくします)
d = Selenium::WebDriver.for :chrome,options: options
d.get("https://www.amazon.co.jp/")
d.find_element(:name,"field-keywords").send_keys("makbook") #検索に文字を入力
sleep 2
d.find_element(:id,"nav-search-submit-text").submit #inputタグに文字を入力後送信します
sleep 3
3.スクリーンショットの撮り方
require "selenium-webdriver"
options = Selenium::WebDriver::Chrome::Options.new
options.headless!
d = Selenium::WebDriver.for :chrome,options: options
d.get("https://www.amazon.co.jp/")
d.find_element(:name,"field-keywords").send_keys("makbook")
d.find_element(:id,"nav-search-submit-text").submit
sleep 5
d.save_screenshot("hoge.png")
d.quit
実行すると「hoge.png」の出来上がりです。
よくある要素の取得など
# ID要素取得
element = driver.find_element(:id, 'id')
# クラス要素取得
element = driver.find_element(:class, 'class')
# タグ要素取得
element = driver.find_element(:tag_name, 'a,div,img,h2')
# リンクテキスト要素取得
element = driver.find_element(:link_text, 'text名')
# a要素のテキストの部分一致する要素を取得する
element = driver.find_element(:partial_link_text, 'text名')
# XPath要素取得
element = driver.find_element(:xpath, "//a[@href='//']")
# css要素取得
elment = driver.find_element(:css, '.class,#id')
#要素が期待する値になるまで待つ
wait = Selenium::WebDriver::Wait.new(timeout: 20)
wait.until { driver.find_element(:id, 'some_id').text == 'Ajaxで生成されたテキスト' }
#windowの移動
driver.switch_to.window(some_id)
# 要素が1つ以上あるか確認
driver.find_elements(:id, 'any-id').size >= 1
#javascriptを使うとき
driver.execute_script(script)
キーボードのエンターキー入力など
driver.find_element(:id, 'any_id').native.send_keys(:return)
driver.find_element(:id, 'any_id').native.send_keys(:enter)
チェックボックス
unless driver.find_element(:id, 'radio-id').selected?
driver.find_element(:id, 'radio-id').click
end
driver.find_element(:id, 'some_check_box').clear # 選択を解除
セレクトボックス
select = Selenium::WebDriver::Support::Select.new(driver.find_element(:id, 'some_select_id'))
select.select_by(:value, 'value')
select.select_by(:text, 'テキスト')
select.select_by(:index, 1)
all_options = select.find_elements(:tag_name, 'option') # すべてのオプション
4.画像を保存
require "selenium-webdriver"
require "open-uri"
def get_items
d = Selenium::WebDriver.for :chrome
d.get("https://www.amazon.co.jp/")
d.find_element(:xpath,"//img")
elements = d.find_elements(:class,"s-image")
url = []
elements.each do |e|
url << e.attribute("src")
end
create_images(url)
end
def create_images(urls)
urls.each_with_index do |url,i|
filename = "test#{i}.jpg"
open(filename,"wb") do |file|
open(url) do |data|
p file.write(data.read)
end
end
end
end
get_items
配列に画像URLを入れて、それをファイルに書き写します。
5.ページ遷移
xpathで要素を取得し、そのhref属性の値にページ遷移するやり方
require "selenium-webdriver"
d = Selenium::WebDriver.for :chrome
texturl = d.find_element(:xpath,'//*[@id="search"]/div[1]/span/a').attribute("href")
sleep 1
d.navigate.to("#{texturl}")
puts d.find_element(:id,"some_id").find_element(:tag_name,"span").text #textがspanタグの場合
sleep 1
d.quit
ページネーションによる要素全取得の方法
driver.get("URL")
page = 1
loop do
page += 1
next = driver.find_element(#pageでボタンの番号があるとこ)
break unless next #要素がなくなったら終わり
next.click
end
6.find_elementでNoSuchElementErrorが発生したとき
webページの表示は条件によって変わります。そんな要素をfind_elementでマッチさせた時期待した要素が見つからない場合があります。
そんなとき、結果が「nil」で返ってくるとおもいきやエラーを処理しないとプログラムがそのまま終了してしまいます。
そこで例外処理を使ってエラーを対処します。
class SomeClass
include Selenium::WebDriver::Error
def some_method(items)
items.each do |i|
begin
title = col.find_element(:class,"title")
rescue NoSuchElementError #エラーを捕捉する
p "エラー発生!"
next
end
puts title.text
end
end
end
seleniumのエラー用のモジュールをincludeしてbegin-endでエラーを捕捉し対処します。
スクレイピングするとき要素が取得できない場合注意する点
iflameのタグがあるとflame位置が間違ってることがあります
7.iflame
frame = driver.find_element(:xpath,"~~")
driver.switch_to.frame(frame)
もしくわ
driver.switch_to.frame
セレクタからフレームを見つけて切り替える
iframe = driver.find_element(:css,'#modal > iframe')
driver.switch_to.frame iframe
nameまたはIDを使う
driver.switch_to.frame 'name or ID'
iflameやflameを終了させ、flameをデフォルトの位置に戻す
driver.switch_to.default_content
data属性の要素取得
d.find_element(:xpath,"~~~~").attribute("data-xxx")
javascriptを実行させる
driver.execute_script('return window.location.pathname')
8.seleniumでスクロール
seleniumでスクレイピングをする際、スクロールをしないと画面がでてこず要素を取得できないこともあるかと思います。
その場合に私がよくやるやり方です、
require "selenium-webdriver"
require "csv"
class TokyoSearch
URL = "https://〜〜"
def initialize
options = Selenium::WebDriver::Chrome::Options.new
options.headless!
@d = Selenium::WebDriver.for :chrome,options: options
end
def run
search
scroll
end
def search
@d.get(URL)
list_names = @d.find_element(:class,"pageContent").find_elements(:tag_name,"li")
list_names.each do |o|
name = o.find_element(:tag_name,"a").find_element(:class,"thumbList__name").text
url = o.find_element(:tag_name,"a").attribute("href")
CSV.open("tokyo.csv","a") do |csv|
csv << ["スタジオ名:#{name}","URL:#{url}"]
end
sleep 1
#ここからスクロールするためのコーディング
last_height = @d.execute_script("return document.body.scrollHeight") #カレントの高さ
while true
1.step(last_height, last_height/10).each do |height|
@d.execute_script("window.scrollTo(0,#{height})")
end
sleep 1
new_height = @d.execute_script("return document.body.scrollHeight") #高さを動かしながらスクロール
if new_height == last_height #ウェブページの更新がなくなったら停止
break
end
last_height = new_height
end
end
end
9.seleniumでページネーション
loop do #ループを回す
begin
pagenation = d.find_element(:class,"a-pagination")
next_button = pagination.find_element(:class,"a-last")
next_link = next_button.find_element(:css,"a")
rescue NoSuchElementError
break #要素が無くなったら停止
end
next_link.click
end
10.新規タブの作成
新規タブを作るにはjavascriptでタブを作ってからそこにURLを開きます
require "selenium-webdriver"
@d = Selenium::WebDriver.for :chrome
@d.execute_script("window.open()")
new_window = @d.window_handles.last
@d.switch_to.window(new_window)
@d.get(url)
クリックして新規タブを作る場合
element.send_keys(:command, :enter)