MAGAZINE
ルーターマガジン
seleniumでログインやスクロールが必要なSNSサイトのスクレイピング
こんにちは。学生エンジニアのhashimotoです。もう平成最後の年末ですね。
今回は、「selenium(セレニウム)を使ってSNSサイトにログインし、タイムラインをスクロールして自動取得する」という流れをご紹介します。マストドンというSNSのQiitadonというインスタンスを例としてプログラムしていきます。
seleniumを使ってログイン
今回はqiitaでマストドンにログイン、qiitaにtwitterでログインという流れでログインしていきます。
まずはgemのrequireやseleniumの設定です。このあたりは過去記事にseleniumの環境構築の記事がありますのでそちらを参照してください。
require 'selenium-webdriver' require 'nokogiri' Selenium::WebDriver::Chrome.driver_path = "/mnt/c/chromedriver.exe" ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36' caps = Selenium::WebDriver::Remote::Capabilities.chrome('chromeOptions' => { args: ["--user-agent=#{ua}", 'window-size=1280x800', '--incognito'] }) # シークレットモード client = Selenium::WebDriver::Remote::Http::Default.new client.read_timeout = 300 driver = Selenium::WebDriver.for :chrome, desired_capabilities: caps driver.manage.timeouts.implicit_wait = 10
さて、ログインしていく部分ですが、基本的にはfind_elementというメソッドで要素を指定しclickやsend_keysといったアクションをしていくだけです。
username = "username" password = "password" # ログインサイトへ飛ぶ driver.navigate.to"https://qiitadon.com/auth/sign_in" sleep 1 # qiitaでログインをクリック driver.find_element(:class, 'button-qiita').click sleep 1 # twitterでログインをクリック driver.find_element(:class, 'btn-twitter-inverse').click # username,passwordの入力欄を指定して入力 username_form = driver.find_element(:name, 'session[username_or_email]') username_form.send_keys(username) password_form = driver.find_element(:name, 'session[password]') password_form.send_keys(password) sleep 1 # ログイン、許可のボタンを押していく driver.find_element(:css, 'input.submit').click sleep 1 driver.find_element(:class, 'btn-success').click
実行結果はこのような感じになります。
ログイン画面
ログイン完了
タイムラインをスクロールしてスクレイピング
ログイン後の画面の真ん中のホームというタイムラインをスクロールしてそれぞれの投稿のテキストを取得していきます。
ここで使用するメソッドがexecute_scriptです。このメソッドは引数のjavascriptを現在開いているブラウザで実行してくれます。ログインの部分もほぼこのメソッドに置き換えることができるほど万能だと思います。
#スクロールする部分を指定 js_scroll_area = "document.getElementsByClassName('scrollable')[0]" # 10回スクロール article_texts = [] 10.times{ sleep 2 driver.execute_script("#{js_scroll_area}.scrollTo(0, #{js_scroll_area}.scrollHeight);") }
これで自由にスクロールしてpage_sourceメソッドでhtmlを取り出しパースすることでスクレイピングができます。
試しに投稿の文をターミナルに表示させてみました。
まとめ
いかがだったでしょうか。open-uriやmechanizeでスクレイピングすることが難しいサイトでもseleniumを使えばうまくいくことがあると思います。SNSのタイムラインを自動保存してみたい(そんな場面があるのか不明ですが)に参考になれば幸いです。SNSによっては制約が厳しい場合もあるのでよく確認して自己責任でプログラミングしていきましょう。
全体のソース
require 'selenium-webdriver' require 'nokogiri' Selenium::WebDriver::Chrome.driver_path = "/mnt/c/chromedriver.exe" ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36' caps = Selenium::WebDriver::Remote::Capabilities.chrome('chromeOptions' => { args: ["--user-agent=#{ua}", 'window-size=1280x800', '--incognito'] }) # シークレットモード client = Selenium::WebDriver::Remote::Http::Default.new client.read_timeout = 300 driver = Selenium::WebDriver.for :chrome, desired_capabilities: caps driver.manage.timeouts.implicit_wait = 10 #twitterのログイン情報 username = "username" password = "password" # ログインサイトへ飛ぶ driver.navigate.to"https://qiitadon.com/auth/sign_in" sleep 1 # qiitaでログインをクリック driver.find_element(:class, 'button-qiita').click sleep 1 # twitterでログインをクリック driver.find_element(:class, 'btn-twitter-inverse').click # username,passwordの入力欄を指定して入力 username_form = driver.find_element(:name, 'session[username_or_email]') username_form.send_keys(username) password_form = driver.find_element(:name, 'session[password]') password_form.send_keys(password) sleep 1 # ログイン、連携のボタンを押していく driver.find_element(:css, 'input.submit').click sleep 1 driver.find_element(:class, 'btn-success').click #スクロールする部分を指定 js_scroll_area = "document.getElementsByClassName('scrollable')[0]" # 10回スクロール article_texts = [] 10.times{ sleep 2 # ページのhtmlを保存してパースしていく html = driver.page_source doc = Nokogiri::HTML(html, nil, 'UTF-8') doc.css("article").css("div.status__content").each do |article| article_texts << article.text end driver.execute_script("#{js_scroll_area}.scrollTo(0, #{js_scroll_area}.scrollHeight);") } driver.quit article_texts.uniq! count = 0 article_texts.each do |text| count += 1 puts count puts text end
CONTACT
お問い合わせ・ご依頼はこちらから