コンビニコミックの発売日をWebスクレイピングで取得してみる

[岩手県立大学とか、岩手の人たち Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ipu) 6日目の記事です。
今日はコミック本の発売日をWebスクレイピングで取得する話をしようと思います。

## コンビニコミックスの料理漫画

在宅勤務がメインになり、外出はスーパーとコンビニの往復だけ、という生活がしばらく続いていました。ちょっとした買い物は近所のコンビニで済ませていたのですが、コンビニで売っている料理漫画をふと購入してみたところ、なかなか面白く、定期的に購入する感じになりました。

個人的には[少年画報社のコンビニコミックス](https://www.shonengahosha.co.jp/comiccs.php?@ps@=none)がお気に入りで、「思い出食堂」「アウトドアごはん」「ひとりごはん」「みんなの食卓」「ときめきごはん」といったシリーズで月イチくらいで出版されているようです。

## 発売日を定期的に把握したい

さて、これらのコミックスの読者投稿コーナーには、次回の書籍の発売日が掲載されています。
しばらくは記載されている発売日をカレンダーにメモしていたのですが、何度か繰り返しているとさすがに面倒に思えてきます…。

少年画報社のWebサイトには[発売日カレンダー](https://www.shonengahosha.co.jp/cal.php?release=2022-01)が公開されており、ここからコミックスの発売日を取得できそうです。

## コミックスの発売日をWebスクレイピングする

### Webページの構造を把握する

まずはWebページというか、欲しい情報を取得するためのURLの構造を把握してみます。

URLは以下の構造になっており、クエリパラメータとして `release=YYYY-MM` を渡すと当該月の情報が取得できるようです。

```
https://www.shonengahosha.co.jp/cal.php?release=2021-12
```

今回は今月・翌月の間の発売日を取得したいので、以下の2つのURLにアクセスすれば良さそうです。
(一桁の数はゼロ埋めして指定するようです)

```
https://www.shonengahosha.co.jp/cal.php?release=2021-12
https://www.shonengahosha.co.jp/cal.php?release=2022-01
```

### HTMLファイルの構造を把握する

次に、HTMLファイルの構造を把握してみます。発売日情報としてほしいのは「日付」「(コミックの)タイトル」です。
この例だと「27（月）」と「みんなの食卓No.43　手軽に♪うどん」になります。

![img.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61137/22d872a9-d1f0-0620-faa7-4fa99d9ec77a.png)

```html
<tr class="border1">
  <th>27（月）</th>
  <td>
    <div style="display:flex;">
      <span class="ico-coco">コンビニコミックス</span>
    </div>
    <b>みんなの食卓No.43　手軽に♪うどん</b><br>
    思い出食堂編集部がお届けする家族と一緒に作れるレシピ付き廉価版コミック！今回の特集は「ホッとする♨うどん」「豚バラのごちそう」「さっと作る和のおかず♪」の3本柱！
...
```

そしてもう一つ、発売日情報としてほしいのは「コンビニコミックス」かつ「思い出食堂」のような料理漫画のもののみです。
以下のように料理漫画以外のコミックスの発売日も含まれるので、欲しい情報のみフィルタリングします。
(個人的には湯けむりスナイパーも番頭のおじさんと元スナイパーのおじさんが朝起きた時に部屋の布団を畳む・畳まないでドタバタ喧嘩を繰り広げたりする ~~面白~~ ハードボイルド漫画です)

![img02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61137/47d04d33-a4e2-50b3-ba72-b721cf3ee346.png)

### 実際にWebスクレイピングしてみる

さっそくWebスクレイピングするためのスクリプトを作成してみます。当月・翌月のURLを渡して処理するための `parse()` を用意し、その関数内でHTMLをスクレイピングし、処理後に欲しいコミックスの情報でフィルタリングします。

```sh
#!/bin/sh

parse() {
    year=$1
    month=$2
    parse_file=$3

    egrep -A3 '<th>.*（|<span class="ico-coco">' $parse_file \
        | egrep '<th>|<span class="ico-coco">|<b>' \
        | grep -B2 '<b>' \
        | grep -v -- '--' \
        | sed \
            -e "s/<th>//g" \
            -e "s/<\/th>//g" \
            -e "s/<span class=\"ico-coco\">//g" \
            -e "s/<\/span>//g" \
            -e "s/<b>//g" \
            -e "s/<\/b>//g" \
            -e "s/<br>//g" \
        | sed \
            -e "s/ //g" \
            -e "s/　//g" \
        | paste - - - \
        | awk '{ print $1","$2","$3 }' \
        | sed -e "s/^/"$year"\/"$month"\//"
}

# 当月・翌月のURLを作成する。
year01=`date "+%Y"`
month01=`date "+%m"`
year02=$year01
month02=$month01

if [ $month01 -eq 12 ]; then
    year02=$((year02+1))
    month02=1
fi

month01=`printf "%02d" $month01`
month02=`printf "%02d" $month02`

URL='https://www.shonengahosha.co.jp/cal.php'
url01="$URL?release=${year01}-${month01}"
url02="$URL?release=${year02}-${month02}"
tmp_file01="${year01}-${month01}.html"
tmp_file02="${year02}-${month02}.html"

# 当月・翌月のHTMLファイルを取得する。
# (ファイルがある場合は取得しない)
mkdir -p working
[ ! -f working/$tmp_file01 ] && curl -L -o working/$tmp_file01 $url01
[ ! -f working/$tmp_file02 ] && curl -L -o working/$tmp_file02 $url02

# 取得したいコミックスのリストを作成する。
cat <<_EOF > working/filter.txt
思い出食堂
ときめきごはん
みんなの食卓
ひとりごはん
_EOF

# HTMLをスクレイピングし、取得したコミックスのリストだけに絞り込む。
(
    parse $year01 $month01 working/$tmp_file01
    parse $year02 $month02 working/$tmp_file02
) | grep -f working/filter.txt
```

実際にスクリプトを走らせると以下の結果が得られます。無事に欲しいコミックスの発売日が取得できました。

```sh
$ ./sample.sh
2021/12/06（月）,コンビニコミックス,年末年始思い出食堂三太の休日♨
2021/12/13（月）,コンビニコミックス,ときめきごはんNo.29みんなで♨トマト鍋
2021/12/27（月）,コンビニコミックス,みんなの食卓No.43手軽に♪うどん
2022/01/11（火）,コンビニコミックス,ひとりごはんNo.40ごきげんパスタ☆
2022/01/31（月）,コンビニコミックス,思い出食堂No.62のんびり♨ラーメン編
```

## まとめ

コミックスの発売日をWebページからスクレイピングする方法を紹介しました。
発売日などのWebページは定期的に生成されることもあり、機械的に生成された静的なHTMLになっていることが多く、比較的Webスクレイピングが行いやすいように思えます。