Help us understand the problem. What is going on with this article?

ChromeとPythonのchromedriver-binaryのバージョンを合わせたい。

はじめに

今回は本当に備忘録な記事です。
ただ、あとあと「こうすれば解決するけど、なぜそうなるんだろう?」みたいなところは追記したいと思います。

また、晴れてChrome 75がリリースされた後でも、この方法が使えるかどうかは確認しようと思います。(20190603 追記)

追記:20191006

設定が最初に記載した時点でも有効かどうか確認、一部修正して、ソースコードをGitHubに上げました。

今回やりたかったこと

  • Python3のDockerイメージを利用して、サッとPython+Seleniumの環境を作りたい
  • headlessモードで動かせればいいので、できるだけシンプルにしておきたい
  • ChromeはGoogleのリポジトリを追加して安定版をインストールする
  • chromedriver-binary (Python用)はpipからインストールする

うまくいかなかった...

タイミングによるのかもしれませんが、Chromeのバージョン(stable)と、pythonのchromedriver-binaryのバージョンがうまく合わないと、うまく動いてくれませんでした...。(2019/06/02時点)

2019/06/02時点で、どちらもオプション無しで入れると、Chromeは Google Chrome 74.0.3729.169、chromedriver-binary は 75.0.3770.8.0に。

この状態でスクリプトを実行しようとすると、エラーが出て動いてくれません...
Message: session not created: This version of ChromeDriver only supports Chrome version 75 と言われて実行できません。
ちなみに、コマンドラインから、google-chrome は起動できます。
Pythonのコードで操作しようとすると残念な結果になってしまいました。

参考: seleniumで「Message: session not created: This version of ChromeDriver only supports Chrome version 75」のエラーが表示される場合の対処法 (@stoneBK7さま: ありがとうございます!)

少し待てばStableのChrome 75がリリースされて、大丈夫になるかと思いますが、方法としてはどちらかにバージョンを揃えるしかありません。

Chromeのバージョン情報を pip install時に引き渡す

もちろん、chromedriver-binaryのサイトからバージョンを確認し、対応する番号を明示してインストールすればいいのですが、Chromeもchromedriver-binaryも気がついたらバージョンが上がっていることがあるので、このへんは毎回手で直さずともできるようにしたい....。

ということで、こんなことを試してみました。

  • 先にChromeをインストール
  • Chromeのバージョンを変数にセット
  • pip install時にバージョン指定

sedでうまく抽出ができなかったので、perlのパターンマッチを利用。1

また、完全に一致ではなく、できるだけ近いバージョンということで 74.0 のようにメジャーバージョン+カンマ1つ分だけを取り出しています。

google-chrome --version | perl -pe 's/([^0-9]+)([0-9]+\.[0-9]+).+/$2/g' > chrome-version
pip install chromedriver-binary~=`cat chrome-version`

実際のファイルたち

実際のDockerfile, docker-compose.yml, サンプルのPythonスクリプトを載せてみます。
以下が一式。

tree
.
├── Dockerfile
├── docker-compose.yml
└── workspace
    └── screenshot-full-20190602074859.png

1 directory, 3 files

Dockerfile

FROM python:3
WORKDIR /workspace

# Chrome入れる
RUN sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN apt-get update && apt-get install -y google-chrome-stable

# Seleniumを入れる
RUN pip install selenium

#
# インストールしたChromeとPythonのchromedriver-binaryのバージョンが合わない
# 場合があるので、google-chromeのバージョン情報から バージョンの
# 近いものを pip installする
#
RUN google-chrome --version | perl -pe 's/([^0-9]+)([0-9]+\.[0-9]+).+/$2/g' > chrome-version
RUN pip install chromedriver-binary~=`cat chrome-version` && rm chrome-version

# フォントを追加(日本語のページをスクリーンショットする場合には追加)
RUN apt-get install -y fonts-ipafont-gothic --no-install-recommends

CMD python /workspace/sample.py

docker-compose.yml

docker-compose.yml
version: '3'
services:
    sample:
        build: .
        volumes:
            - ./workspace:/workspace

workspace/sample.py

workspace/sample.py
#
# Chromeを入れてイメージを作成。その後コンテナで下記を実行。
# workspace/ ディレクトリにスクリーンショットが書き出されます。
#
# docker-compose build
# docker-compose run もしくは
# docker-compose run sample python /workspace/sample.py
#

import chromedriver_binary
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import datetime # yyyymmddHHMMSS のタイムスタンプをファイルに付ける

options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--disable-gpu')

# 今回のDockerではrootで実行なのでこのオプションが必要
options.add_argument('--no-sandbox')

driver = webdriver.Chrome(options=options)

driver.get("https://www.google.com")
print(f'title is {driver.title}')

current_time = datetime.datetime.today()
current_time_str = current_time.strftime("%Y%m%d%H%M%S")

driver.save_screenshot(f'screenshot-full-{current_time_str}.png')

driver.quit()

あとは、ビルドしてスクリプトを実行してみます。(CMDの処理を呼び出し)

docker-compose build
docker-compose run sample
title is Google.  # pythonのスクリプトからの出力。PNGも書き出されます

まとめとか気づきとか

前にもちょこちょことPython+Seleniumで試すことがあったのですが、久しぶりで色々と忘れていたり、気がついてなかったこともいくつかありました。
以下、書き留めておきます。

  • pip installの際に、バージョン指定は =~ や 大なり小なりが使える
    • オプション無しか、完全一致くらいしか意識して使っていませんでした
  • ChromeはHeadlessモードが使えるようになって、これは非常に便利!
    • 2, 3年前までもう少し大変だった気がします....
  • 今回のDockerを使った例では、rootで動かすので、Chromeにオプションが必要
  • 画面をスクロールさせるようなケースのスクリーンショットを撮りたい場合は、画面サイズを一度計測してからリサイズしてキャプチャを取る必要がある(今回は扱っていません)
  • Chromeと各言語に対応したドライバの組み合わせで、もちろんPythonだけじゃなくほかの言語でもうまくいかない時がたまにある(あったのを忘れていた)

などなど。

なお、今回まで Puppeteer は知りませんでした...。(Qiita上でもすでにたくさん記事がありますね!)

Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

今回はPythonのためこうしたメモになりましたが、目的に応じてPuppeteerも試してみたいと思います!


  1. Pythonの公式イメージを使っているのですが、デフォルトでperlが入っています.... 

akiko-pusu
コツコツと学習しながらのメモを書いています。Redmineのプラグイン開発に関連するものが多めです。記事にご興味を持っていただけたら嬉しいです!
https://daily-postit.hatenablog.com
crowdworks
21世紀の新しいワークスタイルを提供する日本最大級のクラウドソーシング「クラウドワークス」のエンジニアチームです!
https://crowdworks.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした