前書き
業務で管理画面からデータを収集するコードを作成していて、最初はselenium+AppleScriptでmacbookをスリープしない設定にして自動化していました。
しかし、こうすると動かしているmacbookは指定の時間には必ずネットワークに接続していないといけません。
どこかに遊びに行く時などにも一応持って行くmacbookで実装してしまったため、前にバッチ処理で使ったことのあったGoogle Cloud Functions(GCF)に移植できないかと思って、色々と試していました。
問題1:Google Cloud Functionsでseleniumを動かす例が少ない
seleniumは、Pythonを含め様々なツールで使えるようになっているウェブサイトのテスト自動化ツールです。
その自動化部分をスクレイピング用のツールとして使われることが度々あります。
そんなseleniumですが、GCFのようなサーバーレス環境で使われる例はあまりありません。
あったとしても、2020年が最終更新のserverless-chromeを使ったものなどです。
問題2:javascriptによるファイルのダウンロードがうまくできない。
なんやかんやserverless-chromeを用いて実装して、実際にデプロイしたところ、目的のボタンを押してもファイルがダウンロードがされませんでした。
GCFは/tmp下以外読み込み専用になっているのが問題なのかと思い、tmpをダウンロード先に指定してもうまく行きませんでした。
serverless-chromeが問題なのか、対象のサイトが問題なのかああでもない、こうでもないと色々と無駄な時間を過ごしました。
解決策
やる気の失せた自分は、chatGPTにseleniumの代替案を聞いたところ、puppeteerというヘッドレスchromeを操作できるnode.jsのライブラリがあるということを知り、pythonにもそのラッパーのpyppeteerが存在することを知りました。
node.jsで動くなら、pythonでも動くやろという精神で実装したところ、実際にうまくいきました。
import asyncio
from pyppeteer import launch
async def main(dl_path):
browser = await launch()
page = await browser.newPage()
cdp = await page.target.createCDPSession()
await cdp.send(
"Page.setDownloadBehavior",
{"behavior": "allow", "downloadPath": dl_path},
)
##以降に、ページへのアクセスからデータをダウンロードするところまでの自動化手順を記述する
...
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
また、asyncを利用しているため、subprocessで上記のプログラムを呼び出すことでGCFで実際に動かすことができます。
import subprocess
def main_process(event={}, context={})
result = subprocess.run(
["python", "data_download_puppeter.py"], capture_output=True, text=True
)
print(result.stdout)
##以降でダウンロードしたファイルを処理する。
最後に
よくよく調べると、ヘッドレスが最近のChromeだと組み込まれてるっぽいので、もしかしたらこんなことをやらずにseleniumで実装できるのかも?