超A&G+の番組表apiを作る(スクレイピング編)

こんにちは，ゆるゆるです

今回は前回作ったラジオ録音の改良をする（番組表から自動で検索，タイトル付けをする）ために超A&G+の番組表をAPI化する記事です。

前回 → https://qiita.com/taittide/items/7219cc9ff6788423ab50

次 → https://qiita.com/taittide/items/c5404b4684f342db2dde


## api化するもの

- all 一週間全てのデータ
- today 今日のデータ
- now 今のデータ

オプション

- isRepeat[bool] 再放送を入れるかどうか
- isBroadcast[bool] 配信中かどうか



## スクレピング後の構造を考える

こっち側で判定するのは **曜日** ，**時間** だけなので jsonでいいかなという感じ。

*月曜0, 火曜1…日曜6*

日付変更で曜日の変更もする

```json
{
  "update_date": "更新日",
  "data": [
    [/* 月曜のデータ */
      {/* 月曜0時のデータ */
        "title": "タイトル",
        "ft": "開始時間(YYYYMMDDHHMM)",
        "to": "終了時間",
        "dur": "番組時間(second)",
        "info": "なし",
        "pfm": "出演者",
        "isRepeat": False
      }
    ], [
      /* 火曜のデータ */
      {...}
    ]
  ]
}
```





## スクレイピングする

超A&G+の番組表見たエンジニアなら吐くと思うんですけど，ソースはこんなんです。

![スクリーンショット 2019-09-03 15.21.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286025/787606b0-184b-a6ee-6713-8972419751f5.png)


各時間ごとにtrで囲まれていて，基本的に7個のtdタグが入っています。しかし，長時間番組がある場合にtdタグの数が変わります。~~無のtdが入ってたら楽だったのに……~~

![スクリーンショット 2019-09-03 15.23.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286025/7ae5d720-1a24-27a0-d76e-e37dabb9ff1a.png)


なので各曜日の終了時間を持っておいて，新しいtdタグの開始時間と比べて配列を更新していきます。

> Aの終了時間がBの開始時間と同じだったらBの終了時間に更新



**！！！！2019-09-03日時点で，「ラジオアニメージュ」だけ放送時間が1分短くなっています。agqrは生放送がないので（ないよね？）多分ミスだと思うのですが，，，**

今回はこいつ↑ だけ別処理にします...

再放送かどうかはtdのclassに入っているもので判定します。

> 'td class="bg-repeat"' ←これ再放送


## ソース

こんな感じになった。githubにもあげます。

```python
#coding: UTF-8
import requests
import json
import datetime
from bs4 import BeautifulSoup

def main():
    body = get_html()
    #body = test()
    soup = BeautifulSoup(body, "html.parser")
    table_body = soup.find("table").find("tbody")
    if table_body is None:
        return
    new_table = create_table(table_body)
    f = open("create_table.json", "w")
    json.dump(new_table, f, ensure_ascii=False)
    f.close()
    

def create_table(table):
    # 月曜の日付（基準）を取得する
    today = datetime.date.today()
    monday = (today - datetime.timedelta(days=today.weekday())).strftime("%Y%m%d")
    # 切り替えの基準を作る
    criterion = datetime.datetime.strptime("06:00", "%H:%M")
    end_times = [datetime.datetime.strptime("06:00", "%H:%M")] * 7
    main_data = []
    main_data2 = []
    for i in range(7):
        main_data.append([])
        main_data2.append([])
    for tr in table.find_all("tr"):
        td_all = tr.find_all("td")
        if (td_all is None) or (len(td_all) == 0):
            continue
        for i in range(len(td_all)):
            td = td_all[i]
            # time が24時を超えた場合のアレ
            time_str = td.find(class_="time").text.replace("\n", "", 3).replace(" 頃", "").split(":")
            if int(time_str[0]) >= 24:
                time_str[0] = format(int(time_str[0]) - 24,  "02")
            time_str = time_str[0] + time_str[1]
            # datetime にする
            tmp_dt = datetime.datetime.strptime(time_str, "%H%M")

            i2 = i
            while(i2<7):
                title = td.find(class_="title-p").text.replace("\n", "", 3)
                pfm = td.find(class_="rp").text.replace("\n", "", 4)
                c = td.get("class")[0]
                isBroadcast = True
                if c == "bg-repeat":
                    # これは再放送
                    isRepeat = True
                elif c == "bg-f":
                    isRepeat = False
                else:
                    isBroadcast = False
                # end_time が1900/1/2になったら分岐する
                if tmp_dt < criterion:
                    tmp_dt2 = tmp_dt + datetime.timedelta(days=1)
                    new_i = (i2 + 1) % 7
                    if tmp_dt2 == end_times[new_i]:
                        # endtime を更新
                        end_times[new_i] += datetime.timedelta(minutes=int(td.get("rowspan")))
                        ft = datetime.datetime.strptime(monday + time_str, "%Y%m%d%H%M") + datetime.timedelta(days=new_i)
                        to = ft + datetime.timedelta(minutes=int(td.get("rowspan")))
                        new_data = {
                            "title": title,
                            "ft": ft.strftime("%Y%m%d%H%M"),
                            "to": to.strftime("%Y%m%d%H%M"),
                            "pfm": pfm,
                            "isBroadcast": isBroadcast
                        }
                        if isBroadcast:
                            new_data["isRepeat"] = isRepeat
                        main_data[new_i].append(new_data)
                        break
                else:
                    if tmp_dt == end_times[i2]:
                        # endtime を更新
                        # ほんまアニメージュ許さんからな
                        if title == "ラジオアニメージュ":
                            end_times[i2] += datetime.timedelta(minutes=30)
                        else:
                            end_times[i2] += datetime.timedelta(minutes=int(td.get("rowspan")))
                        ft = datetime.datetime.strptime(monday + time_str, "%Y%m%d%H%M") + datetime.timedelta(days=i2)
                        to = ft + datetime.timedelta(minutes=int(td.get("rowspan")))
                        new_data = {
                            "title": title,
                            "ft": ft.strftime("%Y%m%d%H%M"),
                            "to": to.strftime("%Y%m%d%H%M"),
                            "pfm": pfm,
                            "isBroadcast": isBroadcast
                        }
                        if isBroadcast:
                            new_data["isRepeat"] = isRepeat
                        main_data2[i2].append(new_data)
                        break
                i2 += 1
                if i2 == 7:
                    # 追加できなかった番組
                    print(td.find(class_="title-p").text.replace("\n", "", 3), ft.strftime("%Y%m%d%H%M"), time_str)
    for i in range(7):
        main_data[i].extend(main_data2[i])
    return main_data
            

def get_html():
    res = requests.get("https://www.agqr.jp/timetable/streaming.html")
    res.encoding = "utf-8"
    return res.text

def test():
    html = open("agqr.html", "r")
    return html.read()

if __name__ == "__main__":
    main()
```





## 終わりに

上のコードを動かすとカレントディレクトリに番組表のjsonが出てきます。  

サブドメイン切ってapi化するぞ





文化放送はなんでこんな構成にしたんだろう…...