Python で Webスクレイピング する場合, これまで mechanize や Beautiful Soup を使う事が多かったのですが Selenium WebDriver を試してみました。
Webクローリング&スクレイピング
Webクローリング&スクレイピングには, 以下のような機能が必要です。
- ドメインからHTMLデータを取り出す
- HTMLを Parseし必要な情報を取り出す
- 情報を格納する
- ページ遷移してこのプロセスを繰り返す
クローリングは, 複数のWebサイトの複数のページにアクセスし情報を取得する技術で, ページ内のリンクを探す, 内部リンクと外部リンクの違いを評価する, 再帰的なページ遷移などの機能が必要になります。特に, クローラが拾ってきた HTML, XML, pdfなどから任意の情報を取得する技術をスクレイピングといいます。
スクレイピング時に JavaScriptを解釈したい場合は Selenium を使う方法があります。
CentOSにSelenium環境を構築
今回はOSXで FirePath を使って xpath を取得しながらコードを書いていったので Firefox にしましたが, AWSやVPSなどGUI環境がない場合, ヘッドレスブラウザの Phantomjs を使う手もあります。
# python 2.7
$ yum -y install xorg-x11-server-Xvfb
$ yum -y install firefox
$ export DISPLAY=:1
$ pip install selenium
$ pip install pyvirtualdisplay
基本的な Selenium の使い方はDocsを参考に。
Ubuntu14.04の場合も書いておきます。
# python 2.7
$ sudo apt-get install xvfb
$ sudo apt-get install firefox
$ export DISPLAY=:1
$ pip install selenium
$ pip install pyvirtualdisplay
FirePathでxpathを取得する
Seleniumでは DOMTree 上の Element を特定するために Locator という仕組みを使います。xpath, CSS 等を指定できます。
今回はFirefoxのFirePath Add-onsを使って xpath を取得してみます。
要素で右クリックを選択して, Inspect in FirePath で簡単に取得できます。
googleトップページのinputタグなら //*[@id=’searchText’] です。一度取得したElementに対して相対pathで移動もできます。
Locatorによる特定では, DOM変更に対して弱い書き方をしないための注意が必要です。
WebサイトにLoginしてみる
Loginする例です。覚えることは割と少ないと思います。
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
driver = webdriver.Firefox()
driver.get('https://example.com')
loginid = driver.find_element_by_xpath("//input[@name='P001']")
password = driver.find_element_by_xpath("//input[@name='P002']")
loginid.send_keys("your-id")
password.send_keys("your-password")
submit = driver.find_element_by_name("loginBtn")
submit.send_keys(Keys.RETURN)
実際には実行してみないとわからないことが色々とありました。
implicitly_wait(time_to_wait) で DOM が構築されるまで wait したり, send_keys() の引数は OSX は数値で OK だったけど CentOS は文字リテラルでないとダメだったり, NoSuchElementExceptionのエラーハンドリング, Proxy設定とか中々大変。
# -*- coding: utf-8 -*-
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
def is_element_present(self, xpath):
try:
self.driver.find_element_by_xpath(xpath)
return True
except NoSuchElementException, e:
return False
異なる環境に移行したり, ブラウザのバージョンを上げたら Selenium との相性もあるので一度は動作確認した方が良さそうです。
WebDriver API との不整合でどうしてもブラウザのバージョンを下げたい時もあるかもしれません。
補足として, 特定バージョンの FireFox をインストールする方法を書いておきます。例として, Firefox 38.0 をインストールしてみます。
$ cd /usr/local
$ wget https://ftp.mozilla.org/pub/firefox/releases/38.0/linux-x86_64/ja/firefox-38.0.tar.bz2
$ tar xvjf firefox-38.0.tar.bz2
$ firefox/firefox -V
$ sudo ln -s /usr/local/firefox/firefox /usr/bin/firefox
Xvfb
メモリ1GBのマシンで定期実行でスクレイピングしていたら `Can’t load the profile や The browser appears to have exited before we could connect, Cannot allocate memory` のエラーにより Selenium の起動が不安定になる事がありました。
FirefoxにSocketで繋ぎにいけない理由は様々だと思いますが, 今回は単にメモリ不足が原因な気がしました。
$ free
total used free shared buffers cached
Mem: 1017764 944412 73352 336 1580 23072
-/+ buffers/cache: 919760 98004
Swap: 1046524 1046524 0
freeでusedが高いので占有しているプロセスを調べみると, 大量のXvfb(Display server)が残っていました。
$ ps -xl --sort -vsize
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1000 12275 1 20 0 226284 2744 poll_s Sl ? 0:23 Xvfb -br -screen 0 1024x768x
0 1000 11596 1 20 0 226276 116 poll_s Sl ? 0:35 Xvfb -br -screen 0 1024x768x
0 1000 5902 1 20 0 225308 9656 poll_s Sl ? 0:08 Xvfb -br -screen 0 1024x768x
0 1000 29754 1 20 0 225300 72 poll_s Sl ? 0:15 Xvfb -br -screen 0 1024x768x
0 1000 20389 1 20 0 225024 11956 poll_s Sl ? 3:09 Xvfb -br -screen 0 1024x768x
0 1000 13461 1 20 0 224996 17928 poll_s Sl ? 0:03 Xvfb -br -screen 0 1024x768x
...
とりあえず killall しましたが, ちゃんとエラー時も display.stop() と driver.close() を忘れないにします。
MongoDBでJournalファイルのサイズ制限
さらに, MongoDBの Journalファイルがディスク容量を喰ってしまう問題もありました。
さくらのVPS メモリ1GB/SSD30GB プランではあまり MongoDB にディスク容量を割きたくありません。
/etc/mongod.conf で smallfilesを trueにすることで, Journalファイルのサイズを制限させます。
$ sudo vim /etc/mongodb.conf
# Enable journaling, https://www.mongodb.org/display/DOCS/Journaling
journal=true
smallfiles=true
設定後, mongod を再起動します。
$ sudo service mongodb restart
mongodb stop/waiting
mongodb start/running, process 17473
[追記] Firefox と Selenium のアップデート
Ubuntu14.04 で Firefox (49.02) と Selenium (3.0.1) の組み合わせにアップデートしたのでコマンドのメモを残しておきます。
$ firefox --version
Mozilla Firefox 38.0
$ sudo apt-get update
$ sudo apt-get install firefox
$ firefox --version
Mozilla Firefox 49.0.2
$ sudo pip install -U pip
$ sudo pip install -U selenium==3.0.1
$ wget https://github.com/mozilla/geckodriver/releases/download/v0.12.0/geckodriver-v0.12.0-linux64.tar.gz
$ tar -xvzf geckodriver-v0.12.0-linux64.tar.gz
$ chmod +x geckodriver
$ sudo cp geckodriver /usr/bin/
[1] pypi/selenium
[2] 明示的な待機 と 暗黙的な待機
[3] mozilla/geckodriver
[4] WebDriver click() vs JavaScript click()