ガンプラ出荷日を自動で Google カレンダーに出力してみた。
以下がガンプラ出荷日を出力した Google カレンダー(こちらが共有用のリンク)です。バンダイが公表している PDF から製品と出荷日をスケジュールします。カレンダーを共有していますのでご自分の Google カレンダーに表示することが可能です。
ただ今は一枚目の PDF だけを対象としています。ガンプラ以外のプラモデルの出荷も PDF には記載されています。プログラム上どれがガンプラかそれ以外かを判別する方法が分からず。過去数ヶ月の PDF を見た限り一枚目だけで対象とすればガンプラの出荷は漏れなくピックアップ出来そうだったので1枚目だけを対象にしています。今後に期待です。プログラムの詳細は後述していますので興味があれば見て頂ければ幸いです。
これをやろうとした背景
最近のガンプラの新商品の予約はミノフスキー・ドライブを軽く超える速度で売り切れます。なので新商品を発売日に手に入れることは若干諦めています(手に入れてもすぐ作らないし)。しかし今は新商品だけじゃなくて過去に発売したガンプラも軒並み売り切れ、もしくは定価以上の価格で売られています。おそらくはコロナの巣ごもり需要やガンプラ生産の資材の供給不足が原因だとは思いますが(あと転売。本当○んでくれないかな。)、欲しい時に手に入らないのは中々ストレスです。
そこで欲しいガンプラを可能な限り手に入れる可能性を高めるためにバンダイが月一で公表している出荷表を毎月見ています。そしてその中に欲しいガンプラがあれば出荷日を Google カレンダーにスケジュールする、ということを続けていたのですが手作業が面倒くさくなってきた(あと PDF がお世辞にも見やすいものではない)ので自動化してみることにしました。
ちなみに今月 7/29 に HGUC Ξガンダムの出荷(7月の出荷表)があります。実際に店頭に並ぶのはその二日後ということらしいのでこれは確実に抑えたいところです。
プログラムとその実行
自動化のためのプログラムが以下です。python で作っています。そして毎月これを実行します。今のところは残念ながら手動です。AWS Lambda で定期実行することを考えています。
※ 7/27 更新
バンダイが公開している PDF が月によってカラム名が変更される可能性があるのでカラム名に依存しないように処理を変更しています。
# coding: UTF-8
from tabula import read_pdf
import pandas as pd
import os
import requests
from bs4 import BeautifulSoup
import re
import datetime
import time
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
# Google Setting
#CALENDER_ID = "*********************@group.calendar.google.com" #prod
CALENDER_ID = "*********************@group.calendar.google.com" #test
SCOPES = ['https://www.googleapis.com/auth/calendar']
# PDF ダウンロード URL を取得
html = requests.get("https://bandai-hobby.net/site/schedule.html")
soup = BeautifulSoup(html.content, "html.parser")
onclick = soup.find(value="PDFダウンロード", type="button")['onclick']
pdf_url = 'https://bandai-hobby.net/site/' + re.sub('window.open\(\'|\'\)$', '', onclick)
# PDF を取得、変換
df = read_pdf(pdf_url)
df = pd.concat(df)
"""
//想定されるデータ構造//
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 <THE ORIGIN> NaN NaN NaN <ガンダムデカール> NaN NaN NaN NaN NaN NaN NaN
2 5057656 9 024HG シャア専用ザクII 赤い彗星Ver. 1,800 10日 NaN 5061135 2 GD MG 汎用-Zシリーズ用 400 26日 NaN
3 5057576 0 025HG ザクII C-6/R6型 1,800 30日 NaN 5057493 0 GD MG 汎用-逆襲のシャア用 400 26日 NaN
4 5058929 3 026HG RX-78-02 ガンダム ORIGIN Ver. 2,300 10日 NaN 5057511 1 GD 逆シャア 連邦 汎用 400 26日 NaN
5 5059154 8 HG ガンダムFSD 2,200 5日 NaN 5057512 8 GD 逆シャア ネオジオン 汎用 400 26日 NaN
(...)
53 <SDW EROES> NaN NaN NaN 5061610 4 MG RX-78-2 Ver.3.0 4,500 5日 NaN NaN
54 5061784 2 曹操ウイングガンダム 倚聖の装 800 26日 NEW NaN NaN NaN NaN NaN NaN
"""
# 1行づつキット名と日付を抽出
dt_now = datetime.datetime.now()
for row in df.fillna('-').itertuples():
for x in row:
# 文字列 "XX日" を含む要素を抽出
if re.compile("[0-9][0-9]?日").search(str(x)):
date_indexs = [i for i, y in enumerate(list(row)) if y == x]
for date_index in date_indexs:
# 出荷日を yyyy-mm-dd に整形
# 出荷日がX月Y日の場合
if '月' in x:
month = str(re.sub('月\d?日', '', x))
day = str(re.sub('\d月|日', '', x))
# 来月が1月の場合は来年
if month == '1':
year = str(dt_now.replace(year = now.year + 1).year)
else:
year = str(dt_now.year)
else:
year = str(dt_now.year)
month = str(dt_now.month)
day = str(re.sub('日', '', x))
date = year + '-' + month + '-' + day
# キット名
# 商品名頭の数字を除去
name = re.sub('^([0-9]|[ ])+', '', row[date_index - 2])
if 'NEW' in str(row[date_index + 1]):
name = '【NEW】' + name
print(name,date)
# 一行に同日があった場合は for 文を抜ける
if len(date_indexs) == 2:
break
# GoogleCalender に予定を登録
# TODO 同一のキット、出荷日の場合は登録をしない処理を入れる
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
service = build('calendar', 'v3', credentials=creds)
calender_event = {
'summary': name,
'location': '',
'description': '',
'start': {
'date': date,
'timeZone': 'Japan',
},
'end': {
'date': date,
'timeZone': 'Japan',
},
}
calender_event = service.events().insert(calendarId=CALENDER_ID, body=calender_event).execute()