はじめに
とりあえずツールを使ってみたい方は、https://github.com/seahawk4747/monitorGunplaReleaseInfo.py/blob/main/README.md をご覧ください。
ガンプラの再販情報がちょっと見づらいので、Pythonを使ったWebスクレイピングの練習として、情報取得の自動化をやってみた。
やりたいこと
- バンダイのWebサイトから出荷予定情報のPDFファイルをダウンロードする。
- ダウンロードしたPDFファイルからデータを抽出し、構造化データへ変換する。
- 必要に応じてデータをフィルタリングできるようにする。(例:RGのみ表示、など。)
化
先行事例
”ガンプラ出荷日を自動で Google カレンダーに出力してみた。” by isurut
isurutさんは、PDFの処理にtabulaを使っていた。私もいろいろ調べた結果、表や日本語処理を考慮するとtabula一択になった。
また、構造化したデータをもとにGoogleカレンダーに情報を追加している。
私はローカルサーバー(Raspberry Piなど)で定期実行させてgmailか何かで通知しようと考えている。
使用するPythonライブラリ
- tabula: PDFファイル内の表をpandasのdata frameに変換する。Javaもインストールする必要あり。
https://tabula-py.readthedocs.io/en/latest/getting_started.html
pip install tabula-py requests beautifulsoup4
処理するデータの観察
以下のようなGoogle検索でデータのサンプルを取得。いろいろデータ処理を試行錯誤した。site:bandai-hobby.net/site/schedule_images/ filetype:pdf after:2020/12/1
tabulaで処理されたデータを見てみると、7桁の商品コード、商品名、価格、出荷日が抽出できている。
基本的には、商品コードを基準にして商品データベースをローカルに構築しよう。
商品名の抽出については、若干ばらつきがある。
商品名は、3HGUC ザクIII改(マシュマー専用機)
のように先頭に数字がついているケースがほとんど。これらは正規表現を使って処理できそう。
旧キットについては、1/100 グフ
のように1/
から始まっているので、場合分けで対応する。
BB戦士については225225 ガンキャノン
のように、3桁の数字が2回連続しているという規則性があるので、
product_name[0:3] == product_name[3:6]
のような条件式で判別できそう。
また、商品名の前方にHG
などの商品カテゴリーを示す文字列があるので、これをうまくつかってカテゴリー毎のフィルタリングに活用する。
旧キットは1/
で始まり、かつ以下のリストの文字列のいずれかに合致することを条件に判別することにする。
(リストにないキットがあれば、コードを修正する。)
old_kit = ['ガンキャノン','ガンダム','シャア専用ザク','量産ザク','ギャン','旧型ザク','ジム',
'ブラウブロ','アッグ','アッガイ','グフ','ドム','シャアゲルググ','量産ゲルググ','シャアズゴック','量産ズゴック',
'ゴック','アッグガイ','ゾゴック','Gアーマー','ボール','ホワイトベース','メカガンダム','メカニックザク',
'リアルガンダム','リアルザク','リアルガンキャノン','リアル旧ザク','リアルジム','リアルドム','リアルゲルググ',
'ランバラル特攻','ジャブローに散る','テキサスの攻防','ア・バオア・クー','ザンジバル','ミニガンダム','シャア専用モビルスーツ',
'武器セット','コアブースター','ララァ専用モビルアーマー','グラブロ','ビグロ','シャアムサイ','巡洋艦ムサイ','ガウ攻撃空母',
'グワジン','マゼラアタック','アッザム','ミディア','ビグザム','サラミス','マゼラン',
'ザクレロ','リックドム','ジュアッグ','ドダイYS','ジオング','ガンタンク','ゾック']
■商品納品予定日は店頭発売日ではございません。
■商品毎の仕入れ、品揃え、入荷日は店舗様によって異なります。ご購入される店舗様にてご確認ください。
■新商品の予定日、発売日(雑誌等で告知されているものを含む)は変更になる場合がございます。"
,商品CODE,Unnamed: 0,"SR
no商 品 名",売価,"ご納品
予定日",備考,商品CODE.1,Unnamed: 1,"SR
no商 品 名.1",売価.1,"ご納品
予定日.1",備考.1,Unnamed: 2,Unnamed: 3
0,,,,,,,,,,,,,,
1,<HG>,,,,,,<サンダーボルト>,,,,,,,
2,5055726,1,3HGUC ザクIII改(マシュマー専用機),"1,500",13日,,5063137,4,HG フルアーマー・ガンダム(TB Ver.),"2,700",23日,,,
3,5056829,8,6HGUC ズゴック 量産型,700,13日,,5063138,1,HG 高機動型ザク“サイコ・ザク”(TB Ver.),"2,700",13日,,,
4,5056830,4,7HGUC ガンタンク,800,9日,,5063139,8,HG アトラスガンダム(TB Ver.),"2,300",23日,,,
5,5056831,1,8HGUC ゴッグ,800,13日,,,,,,,,,
新発売される商品は、NEW
と表示されているが、ちゃんと抽出できている。これは使える。
39,5060402,6,182HGUC ゼータプラス(ユニコーンVer.),"2,400",16日,,5062070,5,EX4EG ストライクガンダム(ライトPKGVer.),500,15日,NEW,,
ややこしいのは、列単位でみた場合、同じ種類のデータが同じ列にない場合が多い点。出荷日を起点にして相対位置でなんとか処理できそうな感触を得た。
WebサイトからPDFファイルのダウンロードURLの特定
Webサイトのソースコードを見れば、わりと簡単に特定できた。 ただし、https://web.archive.org/web/20201024150438/https://bandai-hobby.net/site/schedule.html のように、複数のPDFファイルのリンクが存在したケース(Webアーカイブを使って調査)があり、この辺まで考慮した完全自動化は難しいかも。 一応、複数のファイルが存在してもいいように実装した。<input type="button" onClick="window.open('schedule_images/2021_dec2.pdf')" value="PDFダウンロード" class="width190 margin_t10" />
例外処理
表中の罫線の一部がつぶれてしまって、一つのセルとして認識されてしまうケース。レアケースと思われるので、この例ではアッグ
は無視して前後のデータは正常に処理できるようにした。アッグ
好きだけどごめんなさい。
9,5063158,9.0,1/100 ジム,700,27日,,5062061,3,03SIS-A00 ルルチェ[カラーC],"2,300",20日,,
10,"5063159
5063160",6.0,1/550 ブラウブロ,700,23日,,,,,,,,
11,,2.0,1/100 アッグ,700,27日,,<ポケプラ,,,,,,
12,5063161,9.0,1/100 アッガイ,700,27日,,5058286,7,4セレクトシリーズ ルギア,760,20日,,
ツールの動作
<YEAR>-<MONTH>.csv
というファイルを生成して、対象の月のデータ処理結果について以下の情報を保存。
商品ID | 商品名 | 価格 | 出荷予定日 | TD | カテゴリー | TD |
---|---|---|---|---|---|---|
'5055348' | 'HGUC シナンジュ・スタイン(ナラティブVer.)' | 2600 | datetime.datetime(2021, 12, 27, 0, 0) | 'HG' | 0 |
また、商品情報はproducts_list.csv
というファイルを生成してデータを蓄積していく。
今後取得するPDFファイルの商品名がうまく抽出できない場合は、こちらのデータを使って補正する。
価格改定があった場合は、自動更新するようにする。
5061594,RG RX-78-2 ガンダム,2500,RG
5057927,HG ガンダムエクシア,1200,HG
5061598,RG ガンダムMK-II(エゥーゴ),2500,RG
5059233,HG ガンダムデュナメス,1200,HG
ツールの使用方法
Githubのリンク:
https://github.com/seahawk4747/monitorGunplaReleaseInfo.py/blob/main/README.md
動作環境
Python3.x (Python3.10で動作確認済み)usage: monitorGunplaReleaseInfo.py [-h] [-f FILE] [-c [CATEGORY ...]] [-n]
[Options] Detailed options -h or --help
options:
-h, --help show this help message and exit
-f FILE, --file FILE load stored Bandai's PDF file.
-c [CATEGORY ...], --category [CATEGORY ...]
check specific category. e.g. MG, RG
-n, --new check only NEW products
初回データの取得
バンダイのサイトにアクセスし、最新の出荷予定情報をダウンロードし、構造化データに変換。 初出の商品名があったら、同一フォルダー内の`products_list.csv`に追記。(商品価格が変更された場合は、ファイル内の価格を更新) 商品名の処理がおかしい場合は、`products_list.csv`を編集することで解消できるかもしれない。python monitorGunplaReleaseInfo.py
欲しいものリストの作成
`/monitorGunplaReleaseInfo.py`と同じフォルダー内に`wish_list.csv`というファイルを作成。<商品ID(7桁の数字)>,<購入予定数(最低1)>
モニタリングの実行
- 現在時刻(システム時刻)に基づく直近1週間先までの発売(再販)スケジュールを表示
python monitorGunplaReleaseInfo.py -m
- (a オプション)日付を指定して、指定日を起点に直近1週間先までの発売(再販)スケジュールを表示
python monitorGunplaReleaseInfo.py -m -a 20210101
- (n オプション)新製品のみフィルタリング
python monitorGunplaReleaseInfo.py -m -n
- (c オプション) 商品カテゴリー(例:RG)を指定してフィルタリング
選択可能なカテゴリー:["HG","MG","PG","RG","EG","FG","旧キット","Figure-rise","メガサイズ","BB","SD","Other"]
ガンプラに特化したカテゴリーになっている。
python monitorGunplaReleaseInfo.py -m -c
- (-f オプション)ファイルを指定して、構造化データを表示させる。
python monitorGunplaReleaseInfo.py -f .\2021_nov.pdf
$ python monitorGunplaReleaseInfo.py -m -c 旧キット -a 20211222
<< 発売日:2021-12-23(木) >>
商品ID:5063153, 商品名:1/100 シャア専用ザク, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063154, 商品名:1/100 量産ザク, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063159, 商品名:1/550 ブラウブロ, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063162, 商品名:1/100 グフ, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063164, 商品名:1/100 シャアゲルググ, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063165, 商品名:1/100 量産ゲルググ, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063168, 商品名:1/100 ゴック, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063169, 商品名:1/100 アッグガイ, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063171, 商品名:1/144 Gアーマー, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063173, 商品名:1/60 ガンダム, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063176, 商品名:1/60 シャアゲルググ, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063177, 商品名:1/60 量産ゲルググ, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063178, 商品名:1/60 ドム, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063179, 商品名:1/72 メカガンダム, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063180, 商品名:1/72 メカニックザク, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063181, 商品名:1/100 リアルガンダム, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063186, 商品名:1/100 リアルドム, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063187, 商品名:1/100 リアルゲルググ, 商品カテゴリー:旧キット, 購入予定:0個
Price(only WishList): 0
Price when buying one by one : 23900
<< 発売日:2021-12-27(月) >>
商品ID:5063155, 商品名:1/100 ガンキャノン, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063156, 商品名:1/100 ギャン, 商品カテゴリー:旧キット, 購入予定:0個
*商品ID:5063157, 商品名:1/100 旧型ザク, 商品カテゴリー:旧キット, 購入予定:1個
商品ID:5063158, 商品名:1/100 ジム, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063161, 商品名:1/100 アッガイ, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063182, 商品名:1/100 リアルザク, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063183, 商品名:1/100 リアルガンキャノン, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063184, 商品名:1/100 リアル旧ザク, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063185, 商品名:1/100 リアルジム, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063188, 商品名:1/250 ランバラル特攻, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063189, 商品名:1/250 ジャブローに散る, 商品カテゴリー:旧キット, 購入予定:0個
商品ID:5063190, 商品名:1/250 テキサスの攻防, 商品カテゴリー:旧キット, 購入予定:0個
*商品ID:5063191, 商品名:1/250 ア・バオア・クー, 商品カテゴリー:旧キット, 購入予定:1個
Price(only WishList): 1400
Price when buying one by one : 9100
Total price(only WishList): 1400
Total price when buying one by one : 33000
発売日ごとに分けてリスト表示している。それぞれの末尾に購入金額をWish Listをベースにした場合、すべて1個ずつ買った場合で集計している。あとは、お財布と相談。
この後やること
- Raspberry Piで定期実行するよう設定。
- 直近の出荷情報はメール(多分、gmail)で通知させる。