Go×agoutiで病院の予約戦争に勝利する

f:id:tanabebe:20191224155150p:plain

平日,休日問わず子供の通院時に困った事がある.かかりつけの病院のWeb予約は競争率が高く,予約開始時間に1分でも遅れてしまうと受付が終了してしまう.平日も午前中に休みを取って行かざるを得ない場合もあるのだが,予約を確実に取れる確証がない.
この不安が少しストレスとなっている.そこで今回は病院予約を確実に勝ち取るためにGo×agoutiでWeb予約受付を自動化していく.

実現したいこと

以下のオペレーションを病院予約当日の08:00に処理する.

  1. 病院のWeb予約受付サイトを開く
  2. 自身のアカウントでログインを行う
  3. 予約受付確認を行う
  4. 予約登録を行う

実際に動作させたプログラムはこちらにあげている.

github.com

環境

下環境で動作を確認した.

Name version
macOS Mojave 10.14.6
GoogleChrome 79.0.3945.79
ChromeDriver 79.0.3945.36
Go 1.12.9

導入準備

ChromeDriverのインストール

macなのでbrew installchromedriverをインストールする.

❯❯❯ brew install chromedriver

agoutiをインストール

go getagoutiをインストールする.

❯❯❯ go get github.com/sclevine/agouti

https://agouti.org/

処理の流れ

エラー時については割愛するが,プログラム全体の流れは以下とした.

  1. init関数を実行
  2. waitingファイルを確認し,処理継続可否の判断
  3. configファイルを読み込み
  4. ScrapingListconfigファイルの値を設定
  5. imgファイルの削除
  6. Google Chromeをヘッドレスモードで起動
  7. 予約受付サイトへ遷移
  8. スクリーンショットを取得
  9. 予約受付リンクをクリックし、ログイン画面へ遷移
  10. ログイン情報の入力
  11. スクリーンショットを取得
  12. ログインの実行
  13. スクリーンショットを取得
  14. 予約受付確認を実行
  15. スクリーンショットを取得
  16. 予約受付登録を実行
  17. スクリーンショットを取得する
  18. アクティブなウインドウを閉じる
  19. waitingファイルの削除
  20. Google Chromeを終了する

実装

今回,agoutiで使用した機能を以下に抜粋する.

WebDriver

Webブラウザを操作するためプロセスを制御する.

ChromeDriver

Google ChromeWebブラウザ操作を制御する.ここではヘッドレスとしてブラウザを非表示で起動を行う.オプションを付けずにdriver := agouti.ChromeDriver()とすることでブラウザの表示が行われる.

driver := agouti.ChromeDriver(
        agouti.ChromeOptions(
            "args", []string{
                "--headless", 
            }),
    )

Start

WebDriverプロセスを開始するために必要となる.

Stop

WebDriverプロセスを停止するために必要となる.main関数の最後でプロセスを停止したいのでdeferで処理を行う.

NewPage

WebDriverに対応したPageのレシーバが返ってくる.ここではGoogle Chromeが対象,以降のPage機能を使用するために必要となる.

target, err := driver.NewPage()

Page

Pageは開いているブラウザのセッションを指す.前提条件として以下のように指定したWebDriverとそれに対応するセッションを作成する事が必要となる.

driver := agouti.ChromeDriver()
target, err := driver.NewPage()

URLを渡して画面遷移を行う.

target.Navigate("targetUrl")

CloseWindow

アクティブなウィンドウを閉じる.

target.CloseWindow()

Screenshot

現在開いているページのスクリーンショットを指定したファイル名で保存する.相対パス,もしくは絶対パスで指定する.

target.Screenshot("filePath")

Selection

要素の取得,選択や実行を行う.

引数に指定したアンカー要素のテキストを検索する.

target.FindByLink("今すぐ受付")

Fill

要素を検索し,引数に指定したテキストで埋める.

target.FindByID("user_email").Fill("your mailadress")

Click

要素を検索し,クリックを行う.

target.FindById("sample").Click()

Submit

指定した要素を検索し,フォーム値を送信する.

target.FindByName("commit").Submit()

cronを設定する

maccronの設定を行っていく.実行結果を自分宛にメールで送信,毎日08:00の起動とするようcrontab -eで編集を行う.
※ここではcrontab -eとしているが,このコマンドはeの隣にあるキーがrなのでオプションに-rで大惨事となる可能性があるので,普段からこのオプションに慣れないよう注意して欲しい.

MAILTO = "example@example.com"
0 8 * * * cd /your project path}; bash -l -c 'go run /your project path/main.go'

実行結果

12月21日(土)に実行した結果は以下となった.これはアクセス先のシステムから送信されてきたメール内容を抜粋している.

日付:2019/12/21 8:00

◇ 受付完了のお知らせ

・診察日:2019年12月21日
・時間 :午前診察
・受付No:31

受付Noがニ桁だった事に驚きではあるが,予約受付が無事完了していることが確認出来た.これでストレスは抱えずともかかりつけの病院予約がスムーズに行える.
また,どのような動作となるかヘッドレスオプションを外し,掲載可能範囲の画面を以下に記す.ここではログインを失敗させているが,実際はログイン以降の処理も問題なく行われている事は確認済みだ.

f:id:tanabebe:20191224151445g:plain

課題

Web予約受付を自動化する.と題しプログラムを作成したが,蓋を開けると完全な自動化とは言い難い点があったため,下表に内容をまとめた.

# 詳細
1 macがスリープ状態だとcronが起動しないため,サーバー配置が必要.
3 waitingファイルを手動で作成する運用になっているため,スマートフォンからwaitingファイルを作成するように改善する.
4 スクリーンショットを取得しているものの,現状だと息をしていない.
5 予約受付順にムラがある.

まとめ

課題はあるものの,Web予約受付を自動操作することで確実な予約が可能となった.前日にwatingファイルを仕込んでいく事で当日の不安も解消され,いつもより惰眠も貪る事が出来る.これは良いことづくしと言える.他の展開としては,ブログに書いた内容を連携対象外のWebアプリに書き込んだり,手動のテストも自動化が出来るだろう.実装するにあたって特段難しい事はなかったので,今後も自動化出来る事は積極的に取り組む.

参考

agouti - GoDoc

Agouti

golang.hateblo.jp

program.okitama.org