Python with Seleniumで業務自動化した際のTips集
はじめに
2018年5月頃に突然、高校の友達Aから連絡が来て食事をすることになった。
Aさんはとあるお店を持つまで出世していて、業務を自動化して欲しいというお話だった。
1件目はスプレッドシートだったのでGASを書いて納品。
2件目はWebブラウザをimacrosで自動化している作業があるのだが、
そのプログラムの作成者がメンテナンスできなくなったため、対応して欲しいとのこと。
imacrosを触るのも嫌だったし、詳しく話してみて将来性を考えるとSeleniumの方が良いと判断し、
提案して、合意してもらえた。
最初はJavaで書く予定だったのですが、自分が所属している会社でPython with Seleniumの自動化テストを、
外注していたので、これをいじれる人間になれば社内価値があがるかもと思い、Pythonに変更した。(相談済み)
初めてPython、Seleniumでちゃんと納品できるものを作成したので、そこで得たTips集を残そうと思う。
友達からSeleninmでWeb操作の自動化案件を頂いた(元々はimacrosだったのを無理やり変えた)
— 高木徹 (@TTrpbm) 2018年5月28日
貧弱なPCで並列処理をしなければならないのだが、ブラウザと言語は何が良いのだろう
Chrome+ Pythonが一番環境整えやすくて軽そうなイメージ
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()
詳しいことは以下のサイトを参照してください
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でスクロールするという方法がある。
anker linkもこちらの方法で解決できた
— 高木徹 (@TTrpbm) August 12, 2018
seleniumにてButtonがクリックできない時の対処法 on @Qiita https://t.co/KQ0UQBYVhf
でも、僕の場合は設定ファイルによってクリックする場所が違う。
みたいなコードを書いている部分があったため、
設定によって、初期状態で表示されている場合もあれば、
初期状態で表示されていない場合もある。
なので、スクロールで一番下までみたいのはできない。
場所によってスクロールする場所を決めたりする方法もあるが面倒。
なので僕は、ウィドウサイズを大きくすることで解決した。 (ディスプレイの解像度?などは関係ない)
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お願いします!