- はじめに -
headless Chromeが来た頃、Firefoxのheadless対応の噂がありました。
ヘッドレスFirefoxも近々出るよ / 他46コメント https://t.co/kxeWpaLiTR “PythonでWebスクレイピングする時の知見をまとめておく - Stimulator” https://t.co/eiKaWd1lCb
— 戸田広 (@hiroshitoda) June 26, 2017
そしてheadlessモードが正式に搭載されました。
この記事は、PythonのSelenium.webdriverを使ってFirefoxのheadlessモードを触ろうという導入記事です。
今までCUIでFirefox操作するとなると、xvfbとかVirtual Xを利用してスクリーンを作った上でのFirefox起動が一般的でしたがこれで少し簡単になりますね。
headless Chromeの記事と同じく設定周りについてはいつか追記するかも
環境は以下の通り
//------
Ubuntu 16.04 LTS
Python 3.6.1
pip install selenium (既)
//------
- UbuntuへFirefoxを入れる -
sudo apt-add-repository ppa:mozillateam/firefox-next sudo apt-get update
途中以下のように聞かれるのでEnter
Press [ENTER] to continue or ctrl-c to cancel adding it
こんな感じの出力が出る
取得: http://ppa.launchpad.net/mozillateam/firefox-next/ubuntu xenial InRelease [17.6 kB] 取得: http://ppa.launchpad.net/mozillateam/firefox-next/ubuntu xenial/main amd64 Packages [12.5 kB] 取得: http://ppa.launchpad.net/mozillateam/firefox-next/ubuntu xenial/main i386 Packages [12.5 kB] 取得: http://ppa.launchpad.net/mozillateam/firefox-next/ubuntu xenial/main Translation-en [3,716 B]
インストールする
sudo apt-get install firefox
バージョンは56.0が入る
firefox -v Mozilla Firefox 56.0
試しにheadlessモードを使ってみると以下のような表記が出る。ctrl+cで抜ける。
firefox -headless *** You are running in headless mode.
- Python、Seleniumから使う -
Seleniumから使うにはFirefox用のgeckdriverをインストールしておく必要がある。
geckdriverのバージョンはよしなに。
wget https://github.com/mozilla/geckodriver/releases/download/v0.18.0/geckodriver-v0.18.0-linux64.tar.gz tar -zxvf geckodriver-v0.18.0-linux64.tar.gz sudo cp ./geckodriver /usr/local/bin
geckdriverをインストールしてないと以下のようなエラーが出る。
~~~ raise child_exception_type(errno_num, err_msg) FileNotFoundError: [Errno 2] No such file or directory: 'geckodriver' During handling of the above exception, another exception occurred: ~~~ os.path.basename(self.path), self.start_error_message) selenium.common.exceptions.WebDriverException: Message: 'geckodriver' executable needs to be in PATH.
wbdriverの引数に "-headless" を付けて起動させる。
特に何もしてないと以下のように "/usr/bin/firefox" に配置されているはず。
which firefox /usr/bin/firefox
FirefoxBinaryにpathを指定して、 "add_command_line_options" でheadlessを指定する。
# -*- coding: utf_8 -*- from selenium import webdriver from selenium.webdriver.firefox.firefox_binary import FirefoxBinary binary = FirefoxBinary('/usr/bin/firefox') binary.add_command_line_options('-headless') driver = webdriver.Firefox(firefox_binary=binary) driver.get('http://www.google.com') print(driver.page_source) driver.quit()
HTMLが表示されたら優勝。
ちなみに "-headless" をつけないで "webdriver.Firefox()" で起動させようとすると以下のようになる
~~~ raise WebDriverException( "The browser appears to have exited " "before we could connect. If you specified a log_file in " "the FirefoxBinary constructor, check it for details.") WebDriverException: Message: The browser appears to have exited before we could connect. If you specified a log_file in the FirefoxBinary constructor, check it for details.
上手くSeleniumが起動しない場合の参考は以下
参考:Selenium 3.4.0 Unable to find matching capabilities · Issue #3884 · SeleniumHQ/selenium · GitHub
参考:How to specify Firefox command line options using Selenium WebDriver in Python? - Stack Overflow
- command line argumentとFirefoxProfileについて -
コマンドラインからのオプションとして設定できる項目はMozilla公式の以下にまとまっている。
"-sage-mode" すれば拡張を全て無効化してくれるので便利といえば便利そうである。
引数だけで設定できる項目は少なめで、後述するProfileを使ってゴニョる。
Seleniumのwebdriverには "FirefoxProfile" なるclassがあり、そちらでも設定を記述できる。
一例で書くと以下
# -*- coding: utf_8 -*- from selenium import webdriver from selenium.webdriver.firefox.firefox_binary import FirefoxBinary # profileの記述 profile = webdriver.FirefoxProfile() # useragent指定 profile.set_preference("general.useragent.override", "hogehoge") # 画像を読み込まない profile.set_preference("permissions.default.image", 2) # CSSを使わない profile.set_preference('permissions.default.stylesheet', 2) # Flashを使わない profile.set_preference('dom.ipc.plugins.enabled.libflashplayer.so', 'false') # Firefoxの起動 binary = FirefoxBinary('/usr/bin/firefox') binary.add_command_line_options('-headless') driver = webdriver.Firefox(firefox_binary=binary, firefox_profile=profile) # スクリーンショットを撮って終了 browser.get("hogehoge") browser.save_screenshot("test.jpg") browser.quit()
一応Firefoxのconfig entriesが一番まとまっているのはこの辺
About:config entries - MozillaZine Knowledge Base
Category:Preferences - MozillaZine Knowledge Base
あと公式サポートも参考に
Profiles - Where Firefox stores your bookmarks, passwords and other user data | Firefox Help
一部使えない(?)ものもあるけど参考にはなる
On Firefox in Selenium WebDriver tests with Python do not want images to load and CSS to render – Selenium Webdriver Trainings
Seleniumのオプション周りは以下がちょっと参考になる
selenium.webdriver.firefox.options — Selenium 3.6 documentation
7. WebDriver API — Selenium Python Bindings 2 documentation
加えて"webdriver.FirefoxProfile" で既存の設定ファイルを読み込む事もできる。
またProfileにaddExtensionする事で拡張も使える。Proxyを使う方法もある。
使っていて分かった事として、消化不良でquit()するとプロセスがゾンビ化して、次以降の起動で以下のようなエラーが出るっぽい。
WebDriverException: Message: connection refused
う〜んと悩んで実際にpsコマンドで見てみるとこんな感じ。
$ ps aux | grep “firefox” master 3158 0.0 0.0 0 0 ? Z 16:33 0:00 [firefox] <defunct> master 12661 0.0 0.0 0 0 ? Z 16:53 0:00 [firefox] <defunct> master 13005 0.0 0.0 12948 1088 pts/3 S+ 16:54 0:00 grep --color=auto firefox master 80330 0.0 0.0 0 0 ? Z 15:08 0:00 [firefox] <defunct> master 84301 0.0 0.0 0 0 ? Z 15:12 0:00 [firefox] <defunct> master 101809 0.0 0.0 0 0 ? Z 15:34 0:00 [firefox] <defunct> master 103666 0.0 0.0 0 0 ? Z 15:37 0:00 [firefox] <defunct>
まあUbuntuのFirefoxはGUIで使ってた時もこんな感じだったような気がするから許容範囲か…
- おわりに -
TODO:
ChromeとFirefoxのheadlessを比べるやつを書く
画面操作してみてissue確認する
今後もheadlessで色々できたら良いなと思います。
- 追記
後輩曰く「Firefoxは.lockみたいなファイルができて〜」という話。
何気なくツイートしていると下記のような事がわかった。
テンポラリのprofileディレクトリに
— paihu (@paihu) September 2, 2017
parent.lock
っていうサイズ0のファイルはあった。
試しにファイルをエディタにつかませてFirefox起動させようとしたら
— paihu (@paihu) September 2, 2017
すでに起動してます
みたいなメッセージでたからやっぱり、掴んでるかどうか が判定基準っぽいですね
ChromeやPhontomJSと違い、Firefoxはparent.lockをゾンビ化したプロセスが握っている限り次のFirefoxを起動できないのでこのファイルを開放してやる必要があるらしい。う〜ん…