はんなりPython #43
2021/09/17 nikkie
ブラウザ操作自動化ライブラリHeliumを、 書き方を工夫 🆒して使ったことについて共有します
https://pyconjp.connpass.com/event/221241/ (申込300名(60%)超え🎉)
早期購入特典🍕、まだ間に合う!🇲🇬
まずは導入
ブラウザ操作自動化 ライブラリ
Seleniumのラッパーで、 非常に簡単に書ける!💫(次へ⏬)
https://github.com/mherrmann/selenium-python-helium (2650 star)
from helium import *
start_chrome("google.com") # 1. Chrome立ち上げ、「helium」についてGoogle検索
write("helium selenium github")
press(ENTER)
click("mherrmann/helium") # 検索結果の中からクリック
go_to("github.com/login") # 2. 別の例:GitHubにログイン
write("username", into="Username")
write("password", into="Password")
click("Sign in")
kill_browser() # Chrome終了
https://github.com/mherrmann/selenium-python-helium/blob/master/docs/cheatsheet.md
先日のLTでは、指定したconnpassイベントのコピー(デモしました)
今日デモします :connpassから参加者情報CSVのダウンロード
繰り返し書くコードがある
スクリプトで例外が送出されたときにブラウザのウィンドウが残る
それぞれ、詳しい説明 👉 解決策 の順で話します
Pythonの知識 を使って解決。共有していきます
解決方法=カッコいいPythonの書き方✨
おことわり:ブラウザはFirefoxです(Chromeについても途中言及します)
Heliumを使う中で感じた😖 1点目
start_firefox()
go_to("google.com")
write("helium selenium github")
press(ENTER)
click("Selenium-python but lighter: Helium - GitHub")
start_firefox()
go_to("connpass.com/login")
write(os.getenv("CONNPASS_USERNAME"), into="ユーザー名")
write(os.getenv("CONNPASS_PASSWORD"), into="パスワード")
click("ログインする")
wait_until(Text("あなたのイベント").exists)
start_firefox
start_firefox()
go_to("connpass.com/login")
write(os.getenv("CONNPASS_USERNAME"), into="ユーザー名")
write(os.getenv("CONNPASS_PASSWORD"), into="パスワード")
click("ログインする")
wait_until(Text("あなたのイベント").exists)
start_firefox
を繰り返し書くのをなくしたいstart_firefox
でFirefoxを立ち上げている
👉 デコレータ
関数やクラスの定義の直前に @
で付けるあれ
@decorator
def function():
...
関数を引数に取り、関数を返す 関数 ( 用語集 )
渡された関数の前後にコードを追加できる(ref:『Effective Python 第2版』項目26)
def decorator(function): # 引数に関数
def wrapper(*args, **kwargs):
# functionの前に実行する処理
function(*args, **kwargs)
# functionの後に実行する処理
return wrapper # コードを追加した関数を返す
def show_start_end(func):
@functools.wraps(func) # funcの__name__や__doc__を残すために付ける
def wrapper(*args, **kwargs):
print(func.__name__, "Start")
returned = func(*args, **kwargs)
print(func.__name__, "End")
return returned
return wrapper
@show_start_end
def calculate_bmi(height_m, weight_kg):
return weight_kg / height_m / height_m
>>> calculate_bmi(1.58, 46)
calculate_bmi Start
calculate_bmi End
18.426534209261334
start_firefox
を呼び出すコード
def using_firefox(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_firefox()
func(*args, **kwargs)
return wrapper
@using_firefox
でデコレートすれば、start_firefox
は不要
@using_firefox
def search_helium_repository():
"""HeliumのGitHubリポジトリをGoogle検索して移動する"""
go_to("google.com")
write("helium selenium github")
press(ENTER)
click("Selenium-python but lighter: Helium - GitHub")
@using_firefox
でデコレートすれば、start_firefox
は不要(その2)
@using_firefox
def login_connpass():
"""connpassにログインする(ユーザー名とパスワードは環境変数を想定)"""
go_to("connpass.com/login")
write(os.getenv("CONNPASS_USERNAME"), into="ユーザー名")
write(os.getenv("CONNPASS_PASSWORD"), into="パスワード")
click("ログインする")
wait_until(Text("あなたのイベント").exists)
繰り返しがなくなった!🙌
FirefoxだけでなくChromeでも動かしたい場合、 デコレータを書き換えるだけ で済む!!
@using_chrome # start_chromeを呼び出すデコレータ
def login_connpass():
go_to("connpass.com/login")
write(os.getenv("CONNPASS_USERNAME"), into="ユーザー名")
write(os.getenv("CONNPASS_PASSWORD"), into="パスワード")
click("ログインする")
wait_until(Text("あなたのイベント").exists)
PyCon JP 2021 参加者Tシャツ発売!
— PyCon JP (@pyconjapan) September 15, 2021
PyCon JP 2021は、参加者向けTシャツはオンラインで購入できる形にしました!
9月28日(火)23:59まではお安く手に入れることが出来ます!
この機会に是非どうぞ。 #pyconjphttps://t.co/8UJwyF2J46https://t.co/Siq0xkSFtN pic.twitter.com/QI9K4FGNAt
Heliumを使う中で感じた😖 2点目
kill_browser
start_firefox()
# 退屈なブラウザ操作を代わりにやってくれる処理
kill_browser()
kill_browser
が呼び出されない
start_firefox()
# 退屈なブラウザ操作を代わりにやってくれる処理の途中で
raise Exception
kill_browser() # 呼び出されない
デモします
例外が送出された場合も、 kill_browser
を呼び出したい
👉 コンテキストマネージャ
with
文で使えるオブジェクト ( 用語集 )
with コンテキストマネージャ:
# 処理
...
with
文
with
文はコードのかたまりの前後でコードの初期化と終了処理を実行できるようにします。
https://docs.python.org/ja/3/reference/compound_stmts.html の冒頭より
コードの初期化:オブジェクトの __enter__
メソッド
終了処理:オブジェクトの __exit__
メソッド
with EXPR as VAR:
BLOCK
https://www.python.org/dev/peps/pep-0343/#specification-the-with-statement
EXPR
を評価(コンテキストマネージャを返す)
コンテキストマネージャの __enter__
を呼ぶ(=初期化処理)
BLOCK
を実行
コンテキストマネージャの __exit__
を呼ぶ(=終了処理)
クラス
関数
class MyContextManager:
def __enter__(self):
...
def __exit__(self, exc_type, exc_value, traceback):
...
https://docs.python.org/ja/3/reference/datamodel.html#context-managers
import contextlib
@contextlib.contextmanager
def my_context_manager():
# __enter__ に相当する初期化処理
yield # (yieldで値を返すと as で受け取れる)
# __exit__ に相当する終了処理
https://docs.python.org/ja/3/library/contextlib.html#contextlib.contextmanager
kill_browser
を呼び出すコード
@contextmanager
def using_firefox():
start_firefox()
try:
yield
finally:
kill_browser()
with using_firefox():
# 退屈なブラウザ操作を代わりにやってくれる処理
raise Exception
https://pyconjp.connpass.com/event/221241/
🍕は 9/20(月・祝)まで (まだ知らない方に教えてあげてください)
繰り返し書くコードがある 👉 デコレータ で繰り返し start_firefox
を書かない
スクリプトで例外が送出されたときにブラウザのウィンドウが残る 👉 コンテキストマネージャ でどんなときも kill_browser
紹介したデコレータやコンテキストマネージャはヘルパーとしてPyPIで公開予定です
References & Appendixが続きます(よろしければどうぞ!)
説明できなかった wraps
コンテキストマネージャ
デコレータの最終形
wraps
定期実行
AWS Lambdaで実行
heliumの中にはない(ブラウザ操作自動化だけ)
回答1: スクリプトの自動実行として crontab
で設定
回答2: 定期実行のライブラリ(次へ)
import schedule
schedule.every().day.at("10:30").do(job)
IMO: docker run
する方法でできると思う
PyCon JP スタッフのconnpass操作自動化をいくつも書いて
connpassにログインするコードを繰り返す
go_to("connpass.com/login")
write(os.getenv("CONNPASS_USERNAME"), into="ユーザー名")
write(os.getenv("CONNPASS_PASSWORD"), into="パスワード")
click("ログインする")
wait_until(Text("あなたのイベント").exists)
def logged_in(func):
@wraps(func)
def wrapper(*args, **kwargs):
go_to("connpass.com/login")
write(os.getenv("CONNPASS_USERNAME"), into="ユーザー名")
write(os.getenv("CONNPASS_PASSWORD"), into="パスワード")
click("ログインする")
wait_until(Text("あなたのイベント").exists)
func(*args, **kwargs)
return wrapper
@using_firefox
@logged_in
def automate():
# connpassにログインが必要な退屈なブラウザ操作を代わりにやってくれる処理
logged_in
で automate
の実行前後にコードを追加
logged_in
が返した関数の実行前後に using_firefox
でコードを追加
これにより、 start_firefox
が 一番最初に呼ばれる