ワクチン予約のキャンセル通知システム作ってみた

目的!

  1. コロナワクチン予約をキャンセル待ちしたい!

使ったもの

  • python
  • selenium:予約サイトへのログイン用
  • crontab:定期実行用
  • slack:通知先

やったこと

予約はいっぱいだったので、たまにキャンセルが出て一瞬予約できるっぽい > それを検知してみる! ワクチン予約サイトを定期的にチェックして、空きが出たらslackに通知する仕組みを作る!

準備

使った環境などを雑に並べる

ざっくり処理の抜粋&説明

※とりあえず通知まで行くために、エラー処理やsleep処理はだいぶ適当

headlessブラウザで実行

option = Options()
option.add_argument('--headless')
driver = webdriver.Chrome(chrome_driver_path, options=option)
driver.get(target_url)

自分のワクチン番号&パスワードを使ってログイン(selenium

number = driver.find_element_by_css_selector("input[type=\"tel\"]")
number.send_keys(coupon_number)
sleep(1)
password = driver.find_element_by_css_selector("input[type=\"password\"]")
password.send_keys(passwd)
sleep(1)
driver.find_element_by_css_selector("input[type=\"checkbox\"]").click()
sleep(1)
driver.find_element_by_css_selector("button[type=\"submit\"]").click()
sleep(3)

予約サイトで予約可能な会場の件数を取得(速度はいらないので、そのままselenium

driver.find_element_by_xpath("//*[text()=\" 新規予約\"]").click()
sleep(1)
driver.find_element_by_xpath("/html/body/div[1]/div/div[2]/div/div/button[1]").click()
sleep(2)
driver.find_element_by_css_selector("input[type=\"checkbox\"]").click();
sleep(3)
element = driver.find_element_by_class_name("page-department-search_nav-header__count")
result = re.sub(r"\D", "", element.text)

slack通知

  1. slackワークスペース設定で「Incoming Webhook」を追加してWebhookURLをコピー
  2. slackwebを使って通知(めっちゃ簡単に通知できた!)

参照 Python3でslackに投稿する - Qiita

import slackweb

…

slack = slackweb.Slack(url=slack_webhook_URL)

if result != "0":
    print(result);
    slack.notify(text="<!channel> 空いてるよ!!!")
else:
    print("空いてなかったよ")

定期実行

プログラムの定期実行はなんとなくmacのcrontabを利用!(1分ごとに実行) 通知が来たら素早く予約サイトにアクセスして予約完了!

詳細コードは↓ GitHub - kyamada23/covid19itami-vaccine-check

詰まったところなど

crontabの環境変数問題

crontabで定期実行するも全然動いてくれない。 crontabではPATHが全然通ってなくてpythonが動いていないっぽい ↓を参考に解決!

【保存版】cronでPython3を定時実行する方法&注意すべき4つのポイント | たぬハック

定期実行が途中で止まる問題

1回1回seleniumでブラウザを起動しっぱなしで、ブラウザが溜まり続けてたのが問題だった ちゃんと終わりましょう driver.quit() ↓参照 Selenium - かならずwebdriverを閉じる - Kobe's Public Scrapbox

感想

2,3時間で10件以上キャンセルは出てたっぽいので、F5アタックでも普通に予約できたかも。 でも作った通知システムで予約できたことですごい満足

ちなみに今回は伊丹市のワクチン予約サイトでしてみました。 ワクチン打つと伊丹消滅するらしい コロナワクチン予約システム

GoToEatひょうごが使える店をgoogle mapにしてみた(python/beautifulsoupでwebスクレイピング)

目的!

  1. go to eatひょうごキャンペーンの食事券が使える店を地図で見たい! gotoeat-hyogo.com

  2. webスクレイピングをやってみたい

どっちかというと2番目の目的が先で、何かないかなと思った時に1が出てきた感じです。 python(JupyterNotebook)を使って作ってみました。

出来たこと

go to eatひょうごキャンペーンの食事券が使える店の情報を読み込んで、google mapに読み込むcsvファイルを作成!

作ったgoogleマップがこちら↓ www.google.com

出来なかったこと

googleマップに自動で読み込みは出来ず、手動読み込みに・・・
自動操作はgoogleに禁止されているようです。

準備

使った環境などを雑に並べる

  • mac catalina : 10.15.7
  • chrome : 86
  • python : 3.9.0
  • JupyterNotebook : 6.1.4
  • requests:データ取得ライブラリ
  • BeautifulSoup:データ抽出ライブラリ

※なんとなくAnacondaは使わず、適当にpip3でインストール

ざっくり処理の抜粋&説明

requestsで、キーワード(兵庫県の市町村ごとに)から食事券が使える店を検索

import requests
from bs4 import BeautifulSoup as bs4

…

#検索ページのURL、検索ワード、ページ数から結果のページ情報を取得
url ="(検索ページのURL)"
param = {"keyword" : (検索ワード), "page" : (検索ページ数)} 
res = requests.get(url, param)

主にbeautifulsoupのselectを使って 検索結果&詳細ページから店の情報(店名、住所、電話番号、webサイト、営業時間)を抽出

#ページ解析 
search_result = bs4( res.text, "html.parser")

#店舗情報のエリアを取得(1ページで10件ぐらいあったので、リストで取得して順番に処理)
store_list = search_result.select("div.search-results-list-box")

for store in store_list:
  #店名
  store_name = store.select("p.search-results-list-name")[0].text
  #住所
  store_address = store.select("p.search-results-list-p01")[0].text.replace(" ", "").replace("\n", "").replace("住所:", "")
  ...

あとは各店舗の情報を1行にしてcsvに出力

import csv

csv_filename = "goto_eat_" + search_word + ".csv"
f = open(csv_filename, mode = 'w', encoding='utf-8', errors = 'ignore')
writer = csv.writer(f, lineterminator = '\n')

csv_list = []
csv_list.append(store_name)
csv_list.append(store_address)
…
writer.writerow(csv_list)

詳細コードは↓ https://github.com/kyamada23/goto_eat_hyogo

詰まったところなど

JupyterNotebookがでなんか起動しない問題

jupyter notebookコマンドを実行すると、自動でchromeのページが開いて↓のページが表示されるんですが、 「click here to go to Jupyter.」をクリックしても全然反応してくれない・・・ f:id:potyfl:20201111084858p:plain ターミナルにこれをコピーしろみたいなのがあったのでとりあえずそれ(↓の一番下だけ)をコピペして表示されました。

    To access the notebook, open this file in a browser:
        file:///Users/…
    Or copy and paste one of these URLs:
        http://localhost:8888/?token=…
     or http://127.0.0.1:8888/?token=…

隣の要素取得がしっくりこない

<th>定休日</th>
<td>毎週火曜日</td>

上記で定休日の要素を見つけて、その隣の情報を取得しようとするときに

(定休日の要素).next_sibling だと取得できず(改行が取得されるから?)
(定休日の要素).next_sibling.next_sibling と2回繰り返すと取得できた。

go to eatひょうごの食事券アクセス殺到問題

食事券を購入する時にアクセスが殺到して、全然ページが開かずに買うまでにめちゃめちゃ時間がかかりました。 11/10分からは抽選になっているみたいです。

JupyterNotebook

気軽に実行するには便利なんですが、ファイル形式が独自のものになるので、git管理すると分かりづらい感じがしました。 終わったらpython形式に出力したらいいのかも?

感想

覚えたwebスクレイピングを試してみるにはいい素材だったかなと思います。
他の都道府県のもやってみると面白いかも
ちなみに企業が作った地図検索サービスもあります!(あるかなと思っていたけど、作るまではあえて調べなかった)

go-to-eat-map.com

SQLの準備/実行/読み込み(prepare/execute/fetch)メモ -PHP-

phpのPDO(PHP Data Objects)のかなり簡単なメモです。
メソッド(prepare, execute, fetch)を実行してどんな結果が返ってくるのかあたり。

クエリの作成(PDO::prepare)

実行例:
$dbh->prepare($sql)

クエリーの文(PDOStatementオブジェクト)が返ってくる
基本的に何が入っててもそのまま文にして返してくるっぽい。SQL文として間違っていてもエラーにはならない。
※失敗したらfalseを返すか、例外を投げるっぽいが、どういう時にそうなるのか・・・

参照: https://www.php.net/manual/ja/pdo.prepare.php

クエリの実行(PDO::execute)

実行例:
$dbh->execute($data)

クエリの実行が成功した場合はtrueを返す
クエリの実行が失敗した場合はfalseを返す
※SELECTの結果のレコードが0件の場合もfalse?と思われる

参照: https://www.php.net/manual/ja/pdostatement.execute.php

クエリの結果取得(PDO::fetch)

実行例
$result = $stmt->fetch(PDO::FETCH_ASSOC);

成功した場合、結果のレコードを連想配列として返す(FETCH_ASSOCの場合)
失敗したばい、falseを返す

連想配列として返ってくるので、最初の値を取得する場合はarray_shiftやcurrentなどで指定したり、キーを指定したりする($result['(キー名)'])

https://www.php.net/manual/ja/pdostatement.fetch.php

おまけ

取得した結果を判定するのに参考にしてるページ
empty(), is_null(), isset()の結果一覧: https://php.net/manual/ja/types.comparisons.php