Selenium
#開発
Seleniumとは
Seleniumは、Webブラウザを制御するためのソフトです。Webベースのサービスのテストやスクレイピング等の各種自動化のツールとして用いられます。実際にブラウザを起動して制御するため、GETやPOSTで静的なHTMLを取得するよりも複雑なテストが可能です。
環境構築
Windows 7 + Cygwin + Python3 + Chrome という環境で試しました。cygwinにpython3を入れておきます。たまたま手元の環境にcygwinが入っていたため利用していますが、特にそれが推奨というわけではありません(むしろ心配事が増えそう)。
Getting started - ChromeDriver - WebDriver for Chromeを参照して環境をセットアップします。
ChromeDriver.exeはダウンロードして適当なフォルダに入れます。スクリプトと同じフォルダに置くか、パスの通ったディレクトリに置くのがよいでしょう。
seleniumはpip3で入れます。使っている環境のため、認証プロキシを通すオプションを指定してあります。
code:console.log
$ pip3 install --proxy=http://account:password@example.com:8080 selenium
Collecting selenium
Downloading https://files.pythonhosted.org/packages/80/d6/4294f0b4bce4de0abf13e17190289f9d0613b0a44e5dd6a7f5ca98459853/selenium-3.141.0-py2.py3-none-any.whl (904kB)
100% |████████████████████████████████| 911kB 68kB/s
Requirement already satisfied: urllib3 in /usr/lib/python3.6/site-packages (from selenium)
Installing collected packages: selenium
Successfully installed selenium-3.141.0
サンプルプログラムを動かして動作確認します。ChromeDriverをカレントに置きましたが、パスが通っていないのでwebdriver.Chrome()でパスを指定しています。パスの通ったところに置いてあれば指定不要です。
code:sample.py
import time
from selenium import webdriver
driver = webdriver.Chrome('./chromedriver')
:
:
driver.quit()
もう少し詳しい使い方
下のコードは、私のブログの記事一覧を出力するものです。
code:blog_list.py
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
driver = webdriver.Chrome()
driver.get('http://kb84tkhr.hatenablog.com/archive/category/book')
# driver.implicitly_wait(0)
while True:
titles = driver.find_elements_by_class_name("entry-title-link")
print(*title.text for title in titles, sep="\n")
try:
next_button = driver.find_element_by_link_text("次のページ")
except NoSuchElementException:
break
next_button.click()
driver.quit()
解説していきます。
code:get_page.py
driver = webdriver.Chrome()
driver.get('http://kb84tkhr.hatenablog.com/archive')
ここでは、Chromeを起動してページを表示しています。簡単ですね。
code:get_titles.py
titles = driver.find_elements_by_class_name("entry-title-link")
CSSのクラス名を指定してHTML要素を取得しています。クラス名以外にもいろんな指定の仕方があります。
code:print_titles.py
print(*title.text for title in titles, sep="\n")
titlesにブログ記事のタイトルが入っているので、ひとつずつtitleに取り出して、.textで題名を取得しています。
どのような要素の指定方法があって何が取得できるか、などは先にざっとSelenium API(逆引き)のページを見て、Seleniumでどのようなことができるかを見ておくとあとが楽かと思います。find_element_*は見つかったようそのうち最初のものを返し、find_elements_*は見つかった要素すべてを配列で返します。
code:find_next.py
try:
next_button = driver.find_element_by_link_text("次のページ")
except NoSuchElementException:
break
"次のページ"という文字のリンクを探しています。find系は対応する要素がないとNoSuchElementExceptionを上げます。"次のページ"がないということは全ページ見たということですのでループを脱出します。
code:click.py
next_button.click()
ここに来たということは"次のページ"というリンクがあったということなのでクリックします。
code:quit.py
driver.quit()
Chromeを閉じます。呼ばないでいると、ブラウザを閉じてもプロセスが生きているようで、あとで大量のChromeプロセスを発見してビビります。閉じるまでの間は手で操作することも可能なので、データの入力までは自動でやって、あとは手動で修正して送信、といったこともできそうです。いつquit()を呼ぶかは考えておかないといけませんが。
code:wait.py
# driver.implicitly_wait(0)
Getting Startedのサンプルプログラムでところどころにtime.sleep()が入っているので、HTMLを取得できるまで待つようにしないといけないのかと思いましたが、何もしなくてもうまく待ってくれているようです。implicitly_wait()、explicitly_wait()という関数で、要素が見つかるまでのタイムアウト値を設定できるようになっていることからして、細かい制御が必要になる場面があると思われます。必要になったら勉強します。
関連ブログ
Seleniumのお勉強 (1)
Seleniumのお勉強 (2) 調べる
Seleniumのお勉強 (3) やってみる