ども、KANEです。
最近スクレピング案件が増え、どんどんSeleniumの知識が増えている今日この頃、、
今回はそんな培ったスクレピング知識、困ったときの対応策などを記事にしてまとめてみました!
Seleniumとは
Selenium は、Web ベースアプリケーションのテスト自動化の高速開発をサポートした堅牢なツール群です。Selenium は、Web アプリケーションのテストニーズに特化したテスト機能を豊富に備えています。テスト操作の柔軟性は高く、UI 要素を特定したり、テストの期待値と実際のアプリケーションの動作を比較したりするための多数のオプションを利用できます。
(oss.infoscience.co.jp/seleniumhq/docs/01_introducing_selenium.htmlより引用)
元々はテスト自動化のためのツールだったようで、柔軟にカスタマイズができることからスクレピングする上で非常に有効なツールです。
ブラウザの要素を操作し、データ取得、ページ遷移などさまざまな動きをすることができます。またサポートしているブラウザも多く
- Firefox 2~3
- IE 7~8
- Safari 2~3
- Opera 8~9
- Google Chrome
などがあります。
簡単なスクレピング例
今回はPython3+Selenium+Chromeブラウザを使ってI-SEEDブログを自動操作、スクレピングしてみたいと思います。
環境
- Ubuntu 18.04.3
- Python 3.6.9
- selenium 3.141.0
- chrome 79.0.3945.88
- chromedriver 78.0.3904.70
- chromedriver_binary 78.0.3904.70.0
chromedriver_binaryはchromedriverのパスを自動で通してくれます。
準備
seleniumにはChromeブラウザのバージョンにあったchromedriverインストール必要があります。
またchromedriver_binaryもchromedriverと同じバージョンでインストールしてください。
$ pip install selenium
$ pip install chromedriver_binary
実践
import chromedriver_binary
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument('--headless') #ヘッドレスで起動する
driver = webdriver.Chrome(options=options)
driver.get('https://iseed.jp/blog/') #ページ遷移
driver.save_screenshot("./iseed-blog.png") #現在のページをスクリーンショットする
text = driver.find_element_by_xpath('//*[@id="container"]/div[1]/section/div[1]/div/div/h2').text #ページ内の要素を取得
print(driver.title) #ブログ – 株式会社I-SEED(アイシード)|大阪・心斎橋のWEB制作集団
print(text) #BLOG
driver.quit()#全てのウィンドウを閉じる
以上のコードだけで、簡単にスクリーンショット、サイトタイトルが取得できます。
コマンドについてはドキュメントに色々記載されているのでここでは割愛します。
次は僕が実際遭遇した問題の対応策をまとめています。
陥った問題・対策
エラーが出たときに、Chromeプロセスが止まらない
Chromeのプロセスが継続して動いていると、コードを修正してChromeを動かしたときに正常に動作してくれない場合があります。
import chromedriver_binary
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://iseed.jp/blog/')
print(driver.title)
print(test) #ここでエラー
driver.quit()
このままではdriver.quit()がまで行かず、プロセスが残ります。
8484 pts/0 00:00:00 chromedriver
8490 pts/0 00:00:01 chrome <defunct>
killコマンドなどでプロセスを削除して対応はできますが、なにかとめんどくさい。。。
そんなときは例外処理で対応します。
import logging
import chromedriver_binary
from selenium import webdriver
try:
driver = webdriver.Chrome()
driver.get('https://iseed.jp/blog/')
print(driver.title)
print(test) #ここでエラー
except:
logging.error("traceback",exc_info=True)
finally:
driver.quit()
finallyにdriver.quit()をおいて必ず最終的にChromeを終了させます。
エラー内容も見れるようloggingを追加しています。
.textで取得すると文字が空になる
CSSプロパティがdisplay: noneになっているタグなどを取得する際に起きました。
driver.find_element_by_xpath('//*[@id="hoge"]/div[1]/section/div[1]/div/div/h2').text
そんなときは.get_attribute(“textContent”)で回避できます。
driver.find_element_by_xpath('//*[@id="hoge"]/div[1]/section/div[1]/div/div/h2').get_attribute("textContent")
.textではページ上に表示されている(見える)要素しか取得してくれないので、.get_attribute(“textContent”)を使いましょう。
参考:https://qiita.com/riikunn1004/items/68c7621baaa54cf27230
Sleepしすぎて処理が遅い
要素を取得する際にページ遷移など、ページの表示を待たなければならないとき、sleepを使いがちです。
しかし、そのsleepで設定したタイムは余分な時間ができる可能性があります。
そんなときはオプションで予め最大のスリープ時間を設定しましょう。
driver.implicitly_wait(5)
上のコードで各要素が見つかるまで5秒待ちます。
Chromeが重く、メモリクラッシュが起きる
DockerやHerokuからChromeを動かす機会があるときに起こる問題です。
Chromeの処理は非常に重く、デフォルトのDockerやHeorkuではメモリ不足が原因で正常に動作してくれない場合があります。
そんなときには以下のオプションをを追加しましょう。
options.add_argument('--disable-dev-shm-usage')
デフォルトでDockerやHeorkuでは/dev/shmにメモリを使用するようで64MB、512MBでプログラムによっては足りない場合があります。
上のオプションを使用することで/tmpを使用するようになり、メモリ不足を回避できるようです。
終わりに
一応、僕が遭遇した問題に対する解決策をまとめましたが、もっとベストな方法があるはずです。
他にもっといいのがあれば、コメントよろしくお願いします!