3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-09-03

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

今回は前回作ったラジオ録音の改良をする(番組表から自動で検索,タイトル付けをする)ために超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

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

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

スクレイピングする

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

スクリーンショット 2019-09-03 15.21.33.png

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

スクリーンショット 2019-09-03 15.23.25.png

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

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

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

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

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

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

ソース

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

#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化するぞ

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

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?