Selenium × Python × HerokuでGoogle ClassroomをスクレイピングするBOTを作った


はじめに

こんにちは。今回はSelenium × Python × Herokuを使ってGoogle Classroomをはじめとしたログインしないとアクセスできないサービスをスクレイピングする方法を書き留めておこうと思います。

バッググラウンド

そもそも何がしたいのかを説明します。

わが校では夕飯を食堂で頼めるという仕組みがありまして、毎日(次の日の)申請フォーム(Google Form)がクラスルーム(Google Classroom)送られてきます。当日の午前10:30が締め切りなのですが、意外に忘れる人が多くこれを防止するためにLINE BOTを作ろうと思いました。

今回の趣旨

今回、大きくやりたいことをまとめると以下のような形になると思います。

  • Heroku × Python 環境を構築したい。
  • SeleniumPythonで使いたい。
  • SeleniumHeroku上で動かしたい。
  • SeleniumGoogle Classroom(など)に入りたい。

1. 環境構築

1.1. PackageのインストールとHeroku Appの生成

Python × Herokuの環境構築は多くの方がやられているものと大差ありませんので少し省きつつ構築していきます。また、Pythonpipはインストールされているものとします。

まずは必要なpackageをインストールしていきます。

$ pip install flask
$ pip install selenium

次に、Heroku関係の構築をします。

$ heroku login

出てきたURLにアクセスしてログインしてください。

アプリケーションを生成します。なお、フォルダをその場で生成するのでフォルダ移動を行った後に実行してください。

$ heroku create -a <app-name>
$ cd <app-name>

これでまずは完了です。

1.2. 設定ファイルの準備

次はファイルを作ってアプリとしてdeployする準備をしましょう。

先ほど生成した<app-name>フォルダ内にmain.pyを生成します。

main.pyに仮のコードを書きましょう。

main.py
from flask import Flask, request, abort, jsonify
import os
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "hello world!"

これでhello_worldwebhookができます。これだけだとHerokuでは動いてくれません。ほかの設定ファイルが必要です。

これでpythonのバージョンとインストールしたpackageのバージョンを確認します。

$ python --version
$ pip freeze

以下の三つの設定ファイルを<app-name>フォルダ内に生成します。

runtime.txtに先ほど確認したpythonのバージョンを書き入れます。

runtime.txt
python-3.10.4

requirements.txtHerokuに必要なpackageを教えます。先ほどpip freezeで出力されたもののうち、必要なものだけ抜き出します。

requirements.txt
Flask==2.1.0
selenium==4.1.3

最後にHerokuに実行方法を教えます。

Procfile
web: python main.py

これで初期設定はできました。次はHerokudeployしましょう。

1.3. HerokuへのDeploy

deployにはgitを使いますのでインストールを済ましておいてください。

gitでコミットします。

$ git init
Initialized empty Git repository in .git/

一応、Herokuのリポジトリとつながっているか確認します。

$ git remote -v
heroku  https://git.heroku.com/<app-name>.git (fetch)
heroku  https://git.heroku.com/<app-name>.git (push)

こうなってれば正解です。なって無ければ以下のコマンドをたたいてください。ちなみに僕はなぜかなっていませんでした。

$ heroku git:remote -a <app-name>

最後にdeployの方法ですが、普通のgitと同じようにcommitしてpushするだけです。

$ git add .
$ git commit -m "My first commit"
$ git push heroku main

個人的にはGithub Desktopを使うとやりやすいと思います。(remote先には十分注意してください。)

ちなみに、webhookとなっているのでhttps://<app-name>.herokuapp.com/にアクセスするとhello world!と表示されると思います。

2. Heroku上でSeleniumを動かす

早速Seleniumを動かしていきましょう。
今回はhttps://<app-name>.herokuapp.com/seleniumPOSTに対して応答するように実装します。

main.py
# ...省略
@app.route("/selenium", methods=['POST'])
def seleniumFunc():

  options = webdriver.ChromeOptions()
  options.add_argument('--headless')
  options.add_argument('--no-sandbox')
  options.add_argument("--disable-dev-shm-usage")
  driver = webdriver.Chrome(options=options)
  driver.get("<your-url>")

  # スクレイピング

  # 終了
  driver.quit()

コードはこれだけです。ですが、触ったことある人ならご存じの通り、SeleniumにはGoogle Chrome本体とChrome Driverなるものが必要です。これらは、Heroku側で準備してもらいます。

まずはHerokuダッシュボードの<app-name>プロジェクトの設定を開いてください。

https://dashboard.heroku.com/apps/<app-name>/settings

Buildpacksに以下の二つを追加してください。

https://github.com/heroku/heroku-buildpack-chromedriver.git
https://github.com/heroku/heroku-buildpack-google-chrome.git

これで次のdeployから設定が適応されます。

これでhttps://<app-name>.herokuapp.com/seleniumに適切なPOSTをすることで内容が実行されます。もちろんFlaskの管轄ですのでGETなどの設定は@app.route("/selenium", methods=['POST'])をいじることで可能です。

3. SeleniumでGoogleログインする

最後はちょっとしたオマケです。Googleのサービスは優秀で基本的にGoogle Apps Scriptでいじることができます。ですが、Classroomはそうはいきません。生徒の立場である私からでは何もすることができません。そこでスクレイピングをすることでどうにかしようというわけです。

GoogleはサイトにアクセスするとCookieやキャッシュからログイン情報を読み取ります。Herokuでは毎回要求されますのでこれさえ突破してしまえばなんということはありません。

main.py
# ...省略
@app.route("/selenium", methods=['POST'])
def seleniumFunc():
  # ...省略...
  driver.get("https://classroom.google.com/u/1/c/XXXXXXX")

  # ログイン情報
  login_id = "<your-google-account-mailaddress>"
  login_pw = "<your-google-account-password>"

  # メールアドレス入力
  el_id = driver.switch_to.active_element
  el_id.send_keys(login_id)
  el_id.send_keys(Keys.ENTER)
  sleep(5)

  # パスワード入力
  el_pw = driver.switch_to.active_element
  el_pw.send_keys(login_pw)
  el_pw.send_keys(Keys.ENTER)
  sleep(10)

  # スクレイピング

  # 終了
  driver.quit()

これで好きなようにスクレイピングできます。簡単ですね。

パスワードべた書きで怖いよってかたはHeroku標準の環境変数を利用できます。

$ heroku config:set GOOGLE_MAILADDRESS="<your-google-account-mailaddress>" --app <app-name>
$ heroku config:set GOOGLE_PASSWORD="<your-google-account-password>" --app <app-name>

べた書きの代わりに以下のように書けば利用できます。

main.py
# ...省略...
login_id = os.environ["GOOGLE_MAILADDRESS"]
login_pw = os.environ["GOOGLE_PASSWORD"]

さいごに

今回は「Selenium × Python × HerokuでGoogle ClassroomをスクレイピングするBOTを作った」ということでHeroku環境でもスクレイピングをしたい方におすすめのやり方を書きました。

Chrome DriverChromeが用意されていたり、環境変数が簡単に使えちゃうHeroku最強ですね!なんでもwebhookにできちゃったり...ほんとにもっと早く出会いたかった代物です。

まぁ一つ難点を挙げるとすれば、Add-onが無料のものであってもクレジットカード登録が必要という点ですね。私は学生でクレジットカードないのでAdd-onが利用できませんでした。今回作ったBOTは定時でスクレイピングやデータベース処理などを実行するのですがそれらはすべてGoogle Apps Script側で実装してfetchでこのwebhookをたたいてます。GASはトリガー機能が標準だったり、Google Spread Sheetという最強(笑)のデータベースもあってこっちも扱いやすいです。

ですが、Google関係のことしかできなかったりと未来はHerokuのほうが明るいなと思いました。なにせPythonで書けるというがでかい...

さて、ここまでお付き合いありがとうございました。ご自愛くださいませ。