Seleniumに入門してみた。 Pythonでマルチスレッドのアクセス



DevOpsやCD/CIに欠かせないブラウザでの自動テストのツール Selenium で、複数のウィンドから同一サイトを順次アクセスするツールを作ってみました。 


## 概要

このツールを動作させた時のイメージは次の画像の様になります。 真ん中下のターミナルでPythonのSeleniumのWebDriverが動作しています。 そして、８個のブラウザ画面は、Pythonのプログラムから制御される、FireFoxブラウザで、それぞれ並行動作するスレッドで順番にサイトをアクセスしていきます。

![Selenuim_test.gif](https://qiita-image-store.s3.amazonaws.com/0/42829/8bcd3bf8-5adf-8945-c171-16460b6778c2.gif "Selenuim_test.gif")

この自動テストのためのSelenium API のPythonコードを書いた備忘録です。



## Selenium のインストール

Selenuimの実行環境にはMacOSを利用しました。 MacOSへのSeleniumのインストール方法は、Pythonが動作する環境で、以下だけです。

~~~
pip install -y selenium
~~~

しかし、そう簡単に動作しないので、MacOSの場合、Pythonでバージョンを指定するためのpyenv, そして Python 3.6.0 をインストールするためのMacOSの前提パッケージを入れます。

~~~
brew install pyenv
~~~

そして、すでに導入済みのopensslを一旦削除して再度導入、pyenvでのコンパイルするための環境変数をセットして、インストールを実行する様にします。

~~~
brew uninstall openssl && brew install openssl && CFLAGS="-I$(brew --prefix openssl)/include" LDFLAGS="-L$(brew --prefix openssl)/lib" pyenv install 3.6.2
~~~

参考資料: macOS High Sierra: ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib? https://github.com/pyenv/pyenv/issues/993


それから、 zlibが必要なのですが、 brew install ではなく、次のコマンドを利用します。詳しくは 参考資料のstackoverflow.com を参照してください。

~~~
xcode-select --install
~~~

参考資料: https://stackoverflow.com/questions/23749530/brew-install-zlib-devel-on-mac-os-x-mavericks



## ブラウザのドライバをインストール

### FireFoxのドライバ

FireFoxのドライバは、geckodriver で、GitHub https://github.com/mozilla/geckodriver/releases からダウンロードできます。 アーカイブを解いたら、/usr/local/binへ置いておきます。

~~~
$ sudo cp geckodriver /usr/local/bin
$ sudo chmod +x /usr/local/bin/geckodriver 
~~~

### Chromeのドライバ

こちらは、https://sites.google.com/a/chromium.org/chromedriver/ から入手できます。 同様に /usr/local/binへインストールします。




## Seleniumのインストール

Pythonのバージョンが 3.6以上であることを確認して、

~~~
$ python --version
Python 3.6.2
~~~

Seleniumをインストールします。

~~~
$ pip install -y selenium
~~~


## Pythonのプログラム

コメントを入れておいたので、コードの解説はコメントを読んでください。 このコードは、スレッドを８個立ち上げて、それぞれブラウザを立ち上げて、６個のウェブサイトを順にアクセスした後に、終了します。

FireFoxやChrome を一度に起動するので、メモリやCPUを消費しますので、RAMやCPUなど、資源を十分に搭載したMacで実行してください。 もしかしたら、ハングアップするかもしれないので、起動中のエディタなどのソフトは、実行前に保存しておいてくださいね。

このコードを実行すると、上記のGIF画像の様に、ブラウザが起動して、アクセスが進んでいきます。


~~~
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time
import threading
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait # available since 2.4.0
from selenium.webdriver.support import expected_conditions as EC # available since 2.26.0


th_objs = [] # スレッドの配列
driver = {}  # 各スレッドのwebdriverオブジェクト格納用

# ブラウザのサイズと表示位置
browsers = [
    { "size-x": "640", "size-y": "480", "pos-x": "0",    "pos-y": "0"},
    { "size-x": "640", "size-y": "480", "pos-x": "640",  "pos-y": "0"},
    { "size-x": "640", "size-y": "480", "pos-x": "1280", "pos-y": "0"},
    { "size-x": "640", "size-y": "480", "pos-x": "1920", "pos-y": "0"},
    { "size-x": "640", "size-y": "480", "pos-x": "0",    "pos-y": "480"},
    { "size-x": "640", "size-y": "480", "pos-x": "640",  "pos-y": "480"},
    { "size-x": "640", "size-y": "480", "pos-x": "1280", "pos-y": "480"},
    { "size-x": "640", "size-y": "480", "pos-x": "1920", "pos-y": "480"}
]

# アクセスするウェブサイト
sites =  [
    { "www": "http://www.amazon.co.jp"    },
    { "www": "http://www.google.com"      },
    { "www": "http://www.yahoo.com"       },
    { "www": "http://www.ibm.com"         },
    { "www": "http://www.yahoo.co.jp"     },
    { "www": "http://qiita.com/MahoTakara"}
]


# スレッドでブラウザを制御する関数
def proc(idx):
    browser = browsers[idx]
    tid = threading.get_ident()

    # ブラウザを択一
    #driver[tid] = webdriver.Chrome()
    driver[tid] = webdriver.Firefox()

    # 位置とサイズ指定
    driver[tid].set_window_size(browser['size-x'], browser['size-y'])
    driver[tid].set_window_position(browser['pos-x'], browser['pos-y'])

    # サイトを巡回
    for site in sites:
        print("idx: ", idx, "id:", threading.get_ident(), "title: ", driver[tid].title)
        driver[tid].get(site['www'])
        time.sleep(3)

    # 終了
    driver[tid].quit()        


# メイン
if __name__ == '__main__':

    # スレッドを登録
    for idx in range(0,len(browsers)):
        th_objs.append( threading.Thread( target=proc,args=(idx,)))

    # スレッドの実行開始
    for i in range(0,len(browsers)):
        th_objs[i].start()
~~~


## 参考資料
* SeleniumHQ http://docs.seleniumhq.org/
* Seleniumクイックリファレンス http://www.seleniumqref.com/index.html
* Seleniumとは―読み方、意味、WebDriverの使い方 https://e-algorithm.xyz/selenium/
* Qiita Selenium https://qiita.com/tags/Selenium