エンジニアの卵の成長日記

https://blog.toru-takagi.dev/profile/

Python with Seleniumで業務自動化した際のTips集

はじめに

2018年5月頃に突然、高校の友達Aから連絡が来て食事をすることになった。

Aさんはとあるお店を持つまで出世していて、業務を自動化して欲しいというお話だった。

1件目はスプレッドシートだったのでGASを書いて納品。

2件目はWebブラウザをimacrosで自動化している作業があるのだが、

そのプログラムの作成者がメンテナンスできなくなったため、対応して欲しいとのこと。

imacrosを触るのも嫌だったし、詳しく話してみて将来性を考えるとSeleniumの方が良いと判断し、

提案して、合意してもらえた。

最初はJavaで書く予定だったのですが、自分が所属している会社でPython with Seleniumの自動化テストを、

外注していたので、これをいじれる人間になれば社内価値があがるかもと思い、Pythonに変更した。(相談済み)

初めてPythonSeleniumでちゃんと納品できるものを作成したので、そこで得たTips集を残そうと思う。

Python with Seleniumを書く際のTips

※以下は成功体験から得たものなので、ベストプラクティスではなく、 間違っていることも多くあると思いますが、何卒ご容赦ください

sleepではなくWebDriverWaitを使おう

Seleniumは所詮プログラムなので、ボタンが読み込み終わる前にボタンを押してしまうことがあります。

僕の場合はラズパイ&Wifiでの納品だったので、読み込み速度は不安定です。

そこでsleepを使いたくなってしまいますが、SeleniumにはWebDriverWaitという便利なものがあります。

WebDriverWait(self.browser, 10).until(
    EC.element_to_be_clickable((By.LINK_TEXT, 'ログイン'))
).click()

詳しいことは以下のサイトを参照してください

softwaretest.jp

WebDriverWaitは一つのメソッドで最初に書き、一回だけにしよう

これもちゃんと仕様を調べてないのでわからないのですが、

一つのメソッド内に2回以上WebDriverWaitを記述すると、上手くいかない場合があります。

class Hoge:
    def do(self):
        WebDriverWait()
        WebDriverWait()

if __name__ == '__main__':
    Hoge().do()

上記の書き方だと2つ目以降が動かず、Timeoutしてしまう時があります。

ですが、別のメソッドに分けるとうまくいきます。

class Hoge:
    def done(self):
        WebDriverWait()

    def do(self):
        WebDriverWait()

if __name__ == '__main__':
    Hoge().done()
    Hoge().do()

別ウィンドウで開いてしまうものがある場合

WebDriverWait(self.browser, 10).until(
    EC.element_to_be_clickable((By.LINK_TEXT, '別ウィンドウで開く'))
).click()
self.browser.switch_to_window(self.browser.window_handles[1])

WebDriverのwindow_handlesは立ち上がってるブラウザを配列として持っている。

switch_to_windowで参照するウィンドウを変えることができる。

close()よりquit()を使おう

selenium-dockerのみの現象かもしれませんが、

自動化実行→閉じる→自動化実行→閉じる→...

のような処理を行いたい場合、close()だと自動化→閉じる→何も起きない。

という現象が起きる。quit()で困ったことはないので、基本quit()でいいんじゃないかと思う。

WebDriverには使う時に接続しよう

WebDriverへの接続をインスタンス生成時に行っていました。

class Hoge:
    __init__(self, config):
        self.browser = webdriver.Remote(
            command_executor = 'http://selenium-hub:4444/wd/hub',
            desired_capabilities = DesiredCapabilities.CHROME
        )
    def main(self):
        # schedule機能を利用
if __name__ == '__main__':
    config_json = json.load(open('config.json'))
    for k, v in config_json.imtes():
        Hoge(v).main()

上記のようなコードだったため、自動化コードを実行するタイミングではなく、

プログラムが起動したタイミングでWebDriverと接続をしていました。

僕の案件の場合は、config.jsonに100以上のプロパティがあったので、

起動時に100件以上インスタンスが作成され、接続を行い重くなってしまいました。

当たり前のことですが、実行するタイミングで接続するようにしましょう。

ブラウザはなるべく大きくした方が良い

※2018年8月12日 追記

Seleniumは、ページ内に表示されていないもの?はクリックできないときがあるみたい。

そこで解決として、JavaScriptでスクロールするという方法がある。

でも、僕の場合は設定ファイルによってクリックする場所が違う。

みたいなコードを書いている部分があったため、

設定によって、初期状態で表示されている場合もあれば、

初期状態で表示されていない場合もある。

なので、スクロールで一番下までみたいのはできない。

場所によってスクロールする場所を決めたりする方法もあるが面倒。

なので僕は、ウィドウサイズを大きくすることで解決した。 (ディスプレイの解像度?などは関係ない)

self.browser.set_window_size(1024, 2000)

自動化した際のPythonのTips

他のPythonファイルをインポートする

/hoge
 ┣ main.py
 ┗ config.py

上記のような階層の場合importするだけで簡単に使える

config.py

CONFIG = {
    'aaa' : 1,
    'bbb' : 2
}

main.py

import config

print(config.CONFIG['aaa'])

JSONファイルをインポートする

/hoge
 ┣ main.py
 ┗ config.json

上記のような構造の場合

import json

config_json = json.load(open('config.json'))

スケジュール実行する

pip install schedule

毎週月曜の21時に実行したい場合

import schedule

schedule.every().monday.at('21:00').do(実行したい関数)

最後に

初めてのPython&Seleniumのため、間違っている部分は多くあると思います。

変なところ、質問・ご意見などありましたら、Twitterにご連絡くdさい。

少しでもこの記事が良いと思って貰えたなら、Twitterのフォロー、Kyashお願いします!

twitter.com

f:id:kurowasi2525:20180616200734j:plain