ガンプラの出荷予定情報のチェックをPythonで自動化してみた。


<h1>はじめに</h1>


とりあえずツールを使ってみたい方は、https://github.com/seahawk4747/monitorGunplaReleaseInfo.py/blob/main/README.md をご覧ください。

ガンプラの再販情報がちょっと見づらいので、Pythonを使ったWebスクレイピングの練習として、情報取得の自動化をやってみた。

<h1>やりたいこと</h1>

* バンダイのWebサイトから出荷予定情報のPDFファイルをダウンロードする。
* ダウンロードしたPDFファイルからデータを抽出し、構造化データへ変換する。
* 必要に応じてデータをフィルタリングできるようにする。(例：RGのみ表示、など。)
化

<h1>先行事例</h1>
”ガンプラ出荷日を自動で Google カレンダーに出力してみた。” by isurut

https://doc-sin.life/?p=2860


isurutさんは、PDFの処理にtabulaを使っていた。私もいろいろ調べた結果、表や日本語処理を考慮するとtabula一択になった。
また、構造化したデータをもとにGoogleカレンダーに情報を追加している。
私はローカルサーバー（Raspberry Piなど）で定期実行させてgmailか何かで通知しようと考えている。


<h1>使用するPythonライブラリ</h1>

- tabula: PDFファイル内の表をpandasのdata frameに変換する。Javaもインストールする必要あり。　
https://tabula-py.readthedocs.io/en/latest/getting_started.html



```Python:使用するPythonライブラリのインストール
pip install tabula-py requests beautifulsoup4
```




<h1>処理するデータの観察</h1>
以下のようなGoogle検索でデータのサンプルを取得。いろいろデータ処理を試行錯誤した。

`site:bandai-hobby.net/site/schedule_images/ filetype:pdf after:2020/12/1`


tabulaで処理されたデータを見てみると、7桁の商品コード、商品名、価格、出荷日が抽出できている。
基本的には、商品コードを基準にして商品データベースをローカルに構築しよう。

商品名の抽出については、若干ばらつきがある。
商品名は、`3HGUC ザクIII改(マシュマー専用機)`のように先頭に数字がついているケースがほとんど。これらは正規表現を使って処理できそう。
旧キットについては、`1/100 グフ`のように`1/`から始まっているので、場合分けで対応する。
BB戦士については`225225 ガンキャノン`のように、３桁の数字が２回連続しているという規則性があるので、

```Python:
product_name[0:3] == product_name[3:6]
```
のような条件式で判別できそう。

また、商品名の前方に`HG`などの商品カテゴリーを示す文字列があるので、これをうまくつかってカテゴリー毎のフィルタリングに活用する。

旧キットは`1/`で始まり、かつ以下のリストの文字列のいずれかに合致することを条件に判別することにする。
(リストにないキットがあれば、コードを修正する。）

```
old_kit = ['ガンキャノン','ガンダム','シャア専用ザク','量産ザク','ギャン','旧型ザク','ジム',
'ブラウブロ','アッグ','アッガイ','グフ','ドム','シャアゲルググ','量産ゲルググ','シャアズゴック','量産ズゴック',
'ゴック','アッグガイ','ゾゴック','Gアーマー','ボール','ホワイトベース','メカガンダム','メカニックザク',
'リアルガンダム','リアルザク','リアルガンキャノン','リアル旧ザク','リアルジム','リアルドム','リアルゲルググ',
'ランバラル特攻','ジャブローに散る','テキサスの攻防','ア・バオア・クー','ザンジバル','ミニガンダム','シャア専用モビルスーツ',
'武器セット','コアブースター','ララァ専用モビルアーマー','グラブロ','ビグロ','シャアムサイ','巡洋艦ムサイ','ガウ攻撃空母',
'グワジン','マゼラアタック','アッザム','ミディア','ビグザム','サラミス','マゼラン',
'ザクレロ','リックドム','ジュアッグ','ドダイYS','ジオング','ガンタンク','ゾック']
```

![スクリーンショット 2021-12-09 205838.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155751/597de3d4-9393-13b0-93e0-34307a5011ab.jpeg)


```:tabulaで処理されたデータの一部
■商品納品予定日は店頭発売日ではございません。
■商品毎の仕入れ、品揃え、入荷日は店舗様によって異なります。ご購入される店舗様にてご確認ください。
■新商品の予定日、発売日(雑誌等で告知されているものを含む)は変更になる場合がございます。"
,商品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`と表示されているが、ちゃんと抽出できている。これは使える。

![スクリーンショット 2021-12-09 205940.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155751/a45a1f50-4970-e53d-ba9e-faa3d5ec6be2.jpeg)


```:"NEW"が含まれる行
39,5060402,6,182HGUC ゼータプラス(ユニコーンVer.),"2,400",16日,,5062070,5,EX4EG ストライクガンダム(ライトPKGVer.),500,15日,NEW,,
```

ややこしいのは、列単位でみた場合、同じ種類のデータが同じ列にない場合が多い点。出荷日を起点にして相対位置でなんとか処理できそうな感触を得た。

<h1>WebサイトからPDFファイルのダウンロードURLの特定</h1>
Webサイトのソースコードを見れば、わりと簡単に特定できた。
ただし、https://web.archive.org/web/20201024150438/https://bandai-hobby.net/site/schedule.html のように、複数のPDFファイルのリンクが存在したケース（Webアーカイブを使って調査）があり、この辺まで考慮した完全自動化は難しいかも。
一応、複数のファイルが存在してもいいように実装した。

![スクリーンショット 2021-12-02 101308.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155751/4a57fae7-fed8-2700-e7f0-bb694df0aac1.jpeg)


```:ダウンロードURLが記述されている箇所の例
<input type="button" onClick="window.open('schedule_images/2021_dec2.pdf')" value="PDFダウンロード" class="width190 margin_t10" />
```

<h1>例外処理</h1>

表中の罫線の一部がつぶれてしまって、一つのセルとして認識されてしまうケース。レアケースと思われるので、この例では`アッグ`は無視して前後のデータは正常に処理できるようにした。`アッグ`好きだけどごめんなさい。

![スクリーンショット 2021-11-29 092207.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155751/bc30921a-58fc-eb07-d8e7-4446bd558b91.jpeg)


```
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日,,
```



<h1>ツールの動作</h1>

`<YEAR>-<MONTH>.csv`というファイルを生成して、対象の月のデータ処理結果について以下の情報を保存。





|  商品ID | 商品名 | 価格 | 出荷予定日 | TD | カテゴリー | TD |
| ---- | ---- | ---- | ----| ---- | ---- | ---- |
|  '5055348'  |  'HGUC シナンジュ・スタイン(ナラティブVer.)'  |2600|  datetime.datetime(2021, 12, 27, 0, 0)|   | 'HG' | 0 |



また、商品情報は`products_list.csv`というファイルを生成してデータを蓄積していく。
今後取得するPDFファイルの商品名がうまく抽出できない場合は、こちらのデータを使って補正する。
価格改定があった場合は、自動更新するようにする。

```:products_list.csvのデータの例
5061594,RG RX-78-2 ガンダム,2500,RG
5057927,HG ガンダムエクシア,1200,HG
5061598,RG  ガンダムMK-II(エゥーゴ),2500,RG
5059233,HG ガンダムデュナメス,1200,HG
```


<h1>ツールの使用方法</h1>

Githubのリンク：
https://github.com/seahawk4747/monitorGunplaReleaseInfo.py/blob/main/README.md

<h2>動作環境</h2>
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
```

<h2>初回データの取得</h2>
バンダイのサイトにアクセスし、最新の出荷予定情報をダウンロードし、構造化データに変換。
初出の商品名があったら、同一フォルダー内の`products_list.csv`に追記。（商品価格が変更された場合は、ファイル内の価格を更新）
商品名の処理がおかしい場合は、`products_list.csv`を編集することで解消できるかもしれない。

```
python monitorGunplaReleaseInfo.py
```


<h2>欲しいものリストの作成 </h2>
`/monitorGunplaReleaseInfo.py`と同じフォルダー内に`wish_list.csv`というファイルを作成。

```wish_list.csvのフォーマット
<商品ID（７桁の数字）>,<購入予定数（最低1）>
```


<h2>モニタリングの実行</h2>

- 現在時刻（システム時刻）に基づく直近１週間先までの発売（再販）スケジュールを表示

```
python monitorGunplaReleaseInfo.py -m 
```

- (a オプション）日付を指定して、指定日を起点に直近１週間先までの発売（再販）スケジュールを表示

```2021年1月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をベースにした場合、すべて１個ずつ買った場合で集計している。あとは、お財布と相談。


<h1>この後やること</h1>

- Raspberry Piで定期実行するよう設定。
- 直近の出荷情報はメール(多分、gmail）で通知させる。