Selenium＋ヘッドレスブラウザ(Chrome)によるスクリーンショットの自動取得

# テスト自動化
最近流行のテスト自動化について調べていたら、昔少し名前を聞いたSeleniumがかなり使われており、Docker環境での分散試験や、ヘッドレスブラウザでの高速化など、いろいろな技術を使っていることを知りました。

せっかくDocker環境を構築できるようになってきたので、自分でもすごさを体感したいと思い環境を作ってみましたが、Vagrant＋Docker-composeで環境構築の自動化までやろうとしたら、いろいろ躓いた（まだ躓いたまま…）のでメモ書きとしてこの記事を書きます。

# ヘッドレスブラウザ
「ヘッドレスブラウザ」で検索すれば、いろいろ説明が出てきますが、簡単に言うと画面表示をしないことで、オーバー**ヘッド**をなくした（**レス**）ブラウザです。

# 作成するもの
Pythonのコードから、Selenium＋ヘッドレスブラウザを呼び出し、画面のスクリーンショットを取って終了するような環境を作成します。

Vagrant上にVirtualBoxでCentOS環境を構築し、そこにDockerを乗っけて、UbuntuコンテナにSelenium＋ヘッドレスブラウザをインストールしてスクリーンショットを取得するイメージです。

# フォルダ構成
フォルダ構成は以下の通りです。

```
  +-- docker
  |     +-- selenium
  |     |     +-- chromedriver_linux64.zip（※後述）
  |     |     +-- Dockerfile
  |     |     +-- test.py（スクリーンショット取得プログラムソース）
  |     |
  |     +-- docker-compose.yaml
  |
  +-- image
  |     +-- （取得したスクリーンショットが保存されるディレクトリ）
  |
  +-- Vagrantfile
```

※docker/selenium/chromedriver_linux64.zip は、ダウンロードするChromeのバージョンに併せてダウンロードするドライバを変更します。
　しかし、Chrome最新版のダウンロードURLしか分からず、2019/09/29 時点の最新バージョンが 77.0.3865.90 である為、それに一番近い以下のURLのバージョンを予めダウンロードしています。（過去のバージョンをダウンロードする方法が分かるかた教えてください！）
https://chromedriver.storage.googleapis.com/77.0.3865.40/chromedriver_linux64.zip

　以下にchromedriverのバージョン一覧があります。
　http://chromedriver.storage.googleapis.com/

　恐らく、77.0.3865 まであわせれば大丈夫です。

# Vagrantfile

```lang:vagrantfile
$vm_name = "selenium-test"
$vm_memory_mb = 2048
$vm_cpus = 2
$docker_compose_version = "1.21.2"
$chrome_version = "77.0.3865.40"

Vagrant.configure("2") do |config|

  # ----- Vitual machine name -----
  config.vm.define $vm_name

  # ----- Virtual machine spec -----
  config.vm.provider :virtualbox do |v|
    v.name = $vm_name
    v.memory = $vm_memory_mb
    v.cpus = $vm_cpus
  end

  # ----- Synced folder -----
  #  ホスト側のフォルダとゲストOSのフォルダを同期する設定。
  config.vm.synced_folder "./", "/vagrant"

  # ----- OS -----
  config.vm.box = "centos/7"
  
  # ----- Provisioning (初回の一回だけ実行する) -----
  config.vm.provision :shell, inline: <<-SHELL
    # Dockerインストール
    sudo yum install -y yum-utils device-mapper-persistent-data lvm2
    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    sudo yum makecache fast
    sudo yum -y install docker-ce

    # Dockerデーモン自動起動設定
    sudo systemctl enable docker.service

    # Dockerデーモン開始
    sudo systemctl start docker

    # wget インストール
    sudo yum -y install wget

    # docker-compose をインストール
    sudo wget -L --no-verbose https://github.com/docker/compose/releases/download/#{$docker_compose_version}/docker-compose-`uname -s`-`uname -m`
    sudo chmod +x docker-compose-`uname -s`-`uname -m`
    sudo mv docker-compose-`uname -s`-`uname -m` /usr/local/bin/docker-compose
    sudo chown root:root /usr/local/bin/docker-compose

    # 環境変数（chromeのバージョン）
    export CHROME_VERSION=$chrome_version

    # Dockerイメージのビルド
    sudo /usr/local/bin/docker-compose -f /vagrant/docker/docker-compose.yaml build
  SHELL

  # ----- Provisioning (常に実行) -----
  config.vm.provision :shell, run: "always", inline: <<-SHELL
    # dockerコンテナ群を起動する
    sudo /usr/local/bin/docker-compose -f /vagrant/docker/docker-compose.yaml up -d
  SHELL
end
```

# docker-compose.yaml

```lang:docker-compose.yaml
version: '3.5'

services:

  selenium:
    container_name: selenium-test-container
    image: selenium-test-image
    build: ./selenium
    volumes:
      - ../image:/tmp
    command: python3 test.py
```

# Dockerfile

```lang:Dockerfile
FROM ubuntu:16.04

# default derectory
WORKDIR /

# wgetやunzipをインストール
RUN apt-get update && \
    apt-get -y upgrade && \
    apt-get -y install iputils-ping net-tools wget unzip

# Googleの公開鍵再取得
RUN wget -q https://dl.google.com/linux/linux_signing_key.pub
RUN apt-key add linux_signing_key.pub

# chromedriver のインストール
COPY chromedriver_linux64.zip /chromedriver_linux64.zip
RUN unzip chromedriver_linux64.zip
RUN mv chromedriver /usr/bin/
RUN chmod +x /usr/bin/chromedriver

# chrome のインストール準備
RUN apt-get -y install fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 libcairo2 libcups2 libdbus-1-3 
RUN apt-get -y install libnspr4 libnss3 libxss1 lsb-release xdg-utils

# chrome のインストール
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
RUN dpkg -i google-chrome-stable_current_amd64.deb

# selenium のインストール
RUN apt-get -y install python3-pip && \
    apt-get -y install python3-selenium

# 日本語フォントのインストール
RUN wget --content-disposition IPAfont00303.zip http://ipafont.ipa.go.jp/old/ipafont/IPAfont00303.php  || RET=$? || true && \
    unzip IPAfont00303.zip -d /usr/share/fonts/ && \
    fc-cache -fv

# 不要ファイルの削除
RUN rm -f chromedriver_linux64.zip google-chrome-stable_current_amd64.deb

# スクリーンショット取得プログラムの配置
COPY test.py /test.py
```

# test.py

```python:test.py
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time

options = Options()
options.binary_location = '/usr/bin/google-chrome-stable'
options.add_argument('--headless')
options.add_argument('--no-sandbox')

driver = webdriver.Chrome('chromedriver', chrome_options=options)
# URLのデータを取得
driver.get('https://qiita.com/daichannel/items/08e074a01e8ab41d52f7')
# ここでウィンドウサイズ設定
page_width = driver.execute_script('return document.body.scrollWidth')
page_height = driver.execute_script('return document.body.scrollHeight')
driver.set_window_size(page_width, page_height)

print(driver)
driver.save_screenshot('/tmp/screenshot.png')
driver.quit()
```

# 実行

```
> vagrant up
```

これ一発でimageフォルダ配下に、screenshot.pngファイルが出来ているはずです。

# はまったところ
今回、[こちら](https://qiita.com/xio_yae/items/72c398e9db3ffe7b3136)を参考にさせて頂き、環境を作っていきました。
最初からdocker-composeで自動化したわけではなく、最初は一個一個コマンド打って動作を確認しながら実施しました。その際は、特に躓くこともなくすんなり環境構築できたのですが、docker-composeで自動化して記事にしようとしたところ、かなりいろんなところではまりました。

## 1:ubuntu 18.04 だとダメ
[Teratailでもまだ質問中](https://teratail.com/questions/213829)ですが、上記ではubuntu 16.04で環境構築していますが、当初は18.04で環境構築していました。
ですが、そうするとwgetのインストールや、python3-pipのインストールでエラーが発生して、進めなくなってしまいました。
16.04に変更してみて再度試したところエラーなくインストールできました。
原因が未だ不明なままです。。。

## 2:chromedriverのバージョン
chromedriverは、Chrome向けのWebDriver（Webブラウザを外部から操作する機能）です。その為、Chromeとバージョンを合わせる必要があるのですが、通常最新版しかダウンロードできず、Dockerfileの中で動的にダウンロードライブラリのバージョンを変えることが出来ません。
その為、今回はバージョンを固定し、vagrantfileの中で環境変数を設定してDockerfileまで受け渡していこうと考えましたが、docker-compose.yamlで利用する環境変数は、実行時（docker-compose up）のときに設定されるもので、ビルド(docker-compose build）時には適用されないことが分かり、諦めてバージョン番号をdockerfileに書くようにしました。。。

## 3:Googleの公開鍵取得
Chromeをインストールを試みたタイミングでエラーとなりました。
調査したところ、GoogleのリポジトリのGPG公開鍵をインポートする必要があることが分かりました。

## 4:スクリーンショットの最大化ファイルの取得
Pythonで作成したスクリーンショットの自動取得処理ですが、URLによってエラーになることがありました。
調査したところ、あまり大きい画像になる場合、メモリ不足で落ちているようです。
今回は回避方法まで調べ切れませんでした。

## 5:chromeのインストール
参考にしたサイトでは、簡単にchromeをインストールしていましたが、関連ライブラリが多く、それらを全てインストールしないとエラーが消えませんでした。
その為、上記のdockerfileではchromeインストール前の準備処理があります。

## 6:Dockerコンテナへのファイル受け渡し
dockerホスト（CentOS）からdockerゲスト（Ubuntu）へのファイル受け渡しを、当初はdocker-compose.yamlのVOLUMESパラメータによるディレクトリのマウントで行おうと考えました。
しかし、これはdocker-compose実行時にしかマウントされない為、dockerfileのCOPYコマンドで対応することにしました。
この際、dockerfileのカレントディレクトリからの相対パスで

```lang:dockerfile
COPY ../src/test.py /test.py
```
のように定義したのですが、相対パス先のファイルが参照できず、仕方なくdockerfileと同じディレクトリにtest.pyを配置し、以下のように定義したところきれいに動いてくれました。

```lang:dockerfile
COPY test.py /test.py
```
マニュアルを読めば分かることかもしれませんが、変なところではまってしまいました。

## 7:Proxy
proxy、これが面倒な問題です。
私の利用しているネットワーク環境がProxy環境下である為、Vagrantやdocker-composeに軒並みProxyの設定をしてやらないとVMのダウンロードもままなりません。
Vagrantの vagrant-proxyconf という便利なpluginがあるので、これを使ってこの問題を回避しました。

Proxyとは関係ないですが、会社のインターネット接続制限でGitHubに接続できない問題もあって、chromedriverができなかったりと地味に面倒な環境の中で構築作業を進めた為、ちゃんとスクリーンショットが取れたときは、感動しました。

# URL
## 参考にさせて頂いたサイト
https://qiita.com/xio_yae/items/72c398e9db3ffe7b3136
https://blog.geeko.jp/ftake/2138
https://blog.amedama.jp/entry/2018/07/28/003342

## Teratailの私の質問
[apt-get install python3-pip にてインストールに失敗する](https://teratail.com/questions/213829)