LoginSignup
3
6

More than 3 years have passed since last update.

NHK番組表APIを使ってオリジナル番組表を作って見ました。

Posted at

動機

  • ネタ探しをしていて、Qiita.com:個人でも使える!おすすめAPI一覧の投稿に出会う。
  • コメント欄に「NHK 番組表 APIもいいよ」とあったので覗いて見たら簡単そうだった。
  • REST APIから取得(JSON形式)して、加工・整形してHTML作成まではいい練習と思い。
  • NHK番組APIを使ってオリジナルの番組表を作って見ました。(見てくれは残念ですが、、、)

API keyの取得の流れ

  1. NHK番組APIで、アカウント登録を行います。
  2. メール認証を経て、アプリケーション名:(好きな名称)、URL:(未登録)を登録します。
  3. 登録が完了するとAPI キーが発行されます。これが取得に必要です。

APIの概要

  • 公開中のAPIは以下の4種類になります。
No. 名称 説明
1 Program List API 地域、サービス、日付を指定
2 Program Genre API 地域、ジャンル、日付を指定
3 Program Info API 番組IDを指定
4 Now On Air API 地域、サービスを指定
  • 今回利用するProgram List APIは以下の構成になっています。
https://api.nhk.or.jp/v2/pg/list/{area}/{service}/{date}.json?key={apikey}
パラメータ 説明
area 地域ID(3byte) 130:東京,他46地域
service サービスID(2byte) g1:NHK総合,e1:Eテレ,s1:BS,s3:BSP
date 日付(YYYY-MM-DD形式) 例:2020-04-07
apikey APIキー(32byte) アプリ登録で取得したAPIキー
  • レスポンスフォーマットは、jsonで、利用回数制限は300回/日となっています。
  • 以下の例は、area:130(東京)、service:g1(NHK総合)、date:2020-04-07で取得した一部です。
{
  "list": {
    "g1": [
      {
        "id": "2020040704706",
        "event_id": "04706",
        "start_time": "2020-04-07T04:02:00+09:00",
        "end_time": "2020-04-07T04:30:00+09:00",
        "area": {
          "id": "130",
          "name": "東京"
        },
        "service": {
          "id": "g1",
          "name": "NHK総合1",
          "logo_s": {
            "url": "//www.nhk.or.jp/common/img/media/gtv-100x50.png",
            "width": "100",
            "height": "50"
          },
          "logo_m": {
            "url": "//www.nhk.or.jp/common/img/media/gtv-200x100.png",
            "width": "200",
            "height": "100"
          },
          "logo_l": {
            "url": "//www.nhk.or.jp/common/img/media/gtv-200x200.png",
            "width": "200",
            "height": "200"
          }
        },
        "title": "ダーウィンが来た!「波乱のライオン学園に潜入!百獣の王を養成!!」",
        "subtitle": "成長まっただ中のライオンの子どもたちが、群れの中で先生役の大人から狩りの技や子育て術を学ぶ。不真面目な生徒は退学処分に!?学園ドラマ顔負けの波乱の日々に密着!",
        "content": "新年度1本目は、ライオンの「学校」をご紹介。成長真っ最中のライオンの子どもたちが、群れの中で先生役の大人から狩りや子育て、ライバル撃退法まで、生きるためのあらゆるスベを学ぶ。でもライバル・ハイエナに全く歯が立たなかったり、狩りでは姿が丸見えで獲物にあっさり逃げられたり、実践形式の授業にみんなタジタジ。さらに不真面目な生徒はまさかの退学処分に!?学園ドラマ顔負けの波乱の授業に潜入!歌:MISIA",
        "act": "【語り】和久田麻由子,龍田直樹,豊嶋真千子,山田孝之,水瀬いのり",
        "genres": [
          "0802",
          "1000"
        ]
      },
{以下、省略}

データ取得

  • 取得したAPIキーで、Program List APIを叩く。
  • 取得サービスは、g1:総合1、e1:Eテレ、s1:BS1、s3:BSプレミアムの4局です。
  • pandasのjson_normalize()を使って、json形式をデータフレームに変換します。
import pandas as pd
import json
import requests
import datetime

# 取得したAPIキーをセット
apikey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

# 130:東京
area = '130'
# g1:NHK総合1,e1:Eテレ,s1:BS,s3:BSP
service = ['g1','e1','s1','s3']
# 日時
date = datetime.date.today()

all_results = pd.DataFrame(index=None, columns=[])
for i in range(len(service)):
    url = 'https://api.nhk.or.jp/v2/pg/list/{0}/{1}/{2}.json?key={3}'.format(area,service[i],date,apikey)
    request_get = requests.get(url)
    if request_get.status_code != 200:
        print('NHK番組表APIのデータが取得出来ません。')
        break
    result = pd.json_normalize(request_get.json(), ['list',[service[i]]])
    all_results = pd.concat([all_results, result])

all_results = all_results[~all_results['title'].str.contains('放送休止')]

取得したデータ概要:

<class 'pandas.core.frame.DataFrame'>
Int64Index: 235 entries, 0 to 37
Data columns (total 22 columns):
 #   Column                 Non-Null Count  Dtype 
---  ------                 --------------  ----- 
 0   id                     235 non-null    object
 1   event_id               235 non-null    object
 2   start_time             235 non-null    object
 3   end_time               235 non-null    object
 4   title                  235 non-null    object
 5   subtitle               235 non-null    object
 6   content                235 non-null    object
 7   act                    235 non-null    object
 8   genres                 235 non-null    object
 9   area.id                235 non-null    object
 10  area.name              235 non-null    object
 11  service.id             235 non-null    object
 12  service.name           235 non-null    object
 13  service.logo_s.url     235 non-null    object
 14  service.logo_s.width   235 non-null    object
 15  service.logo_s.height  235 non-null    object
 16  service.logo_m.url     235 non-null    object
 17  service.logo_m.width   235 non-null    object
 18  service.logo_m.height  235 non-null    object
 19  service.logo_l.url     235 non-null    object
 20  service.logo_l.width   235 non-null    object
 21  service.logo_l.height  235 non-null    object
dtypes: object(22)
memory usage: 42.2+ KB

データ加工

  • 日付形式に置き換え、放送時間の算出、ページ内リンクのためのHTMLコード列の追加
# 日付形式に変換、放送時間の計算
all_results['start_time'] = pd.to_datetime(all_results['start_time'], format='%Y/%m/%d %H:%M')
all_results['end_time'] = pd.to_datetime(all_results['end_time'], format='%Y/%m/%d %H:%M')
all_results['airtime'] = all_results['end_time'] - all_results['start_time']
all_results['link'] = all_results['id']
# ページ内リンク情報の作成
func = lambda x: x.replace(x, f'<div id="{x}">番組詳細</div>')
all_results['link'] = all_results['link'].apply(func)

# 月・日。時間・分を取得する。さらに番組表の時間行名を追加する
tmp = pd.concat([all_results['start_time'].dt.month ,all_results['start_time'].dt.day,
                 all_results['start_time'].dt.hour, all_results['start_time'].dt.minute,
                 (all_results['start_time'].dt.day.astype(str)+all_results['start_time'].dt.strftime('%H'))], axis=1)
tmp.columns = ['month', 'day', 'hour', 'minute','time_bins']

# 取得データと新規の列を結合する
all_results = pd.concat([all_results, tmp], axis=1)

# 番組表ための集約
data = all_results.iloc[:,[23,0,1,2,3,4,5,6,7,8,9,10,11,12,22,24,25,26,27,28]]

データ整形(その1)

  • 番組表の必要データが揃えられたので、最終形のテーブルを作成してHTML形式保存します。
# 最終の番組表のデータフレーム作成
tv_index = data.time_bins.sort_values().unique()
tv_table = pd.DataFrame(index=tv_index, columns=['H', 'g1', 'e1', 's1', 's3']).fillna('')

# コンテンツを結合して時間帯に挿入する。
cell = ''
for s in range(len(service)):
    for i in range(len(tv_table.index)):
        tmp = data[(data['time_bins'] == tv_table.index[i]) & (data['service.id'] == service[s])]

        for c in range(len(tmp)):
            cell += '[' + str(tmp['minute'].iloc[c]) + '~]'
            cell += tmp['title'].iloc[c]
            cell += f'<a href="#{tmp["id"].iloc[c]}">▼</a><br>'
        tv_table[service[s]].iloc[i] = cell
        cell = ''

# 時間帯追加
for h in range(len(tv_index)):
    tv_table['H'].iloc[h] = int(tv_index[h][-2:])
tv_table.columns = ['H', 'NHK総合', 'Eテレ', 'BS', 'BSP']

データ整形(その2)

  • 番組タイトルから、ページ内リンクとして番組詳細情報を提供するためテーブル作成します。
detail_table = data.stack().reset_index()
detail_table.drop(columns='level_0', inplace=True)
detail_table.columns = ['種別', '値']
detail_table.set_index('種別', drop=True, inplace=True)

データ整形(その3)

  • pandasのto_htmlを使って、htmlファイルを作成します。

  • htmlファイルの定義情報

html_template = '''
<!doctype html>
<html lang="ja">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <title>私製番組表</title> <!-- #1 -->
  </head>
  <body>
    <h2>&nbsp;NHK番組表APIを使ってオリジナル番組表を作って見ました。{date}版</h2> <!-- #2 -->

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
    <style type="text/css"> th {{ text-align: center; background-color: #f5f5f5}}</style>
    <div class="container-fluid"> <!-- #3 -->
        {table}
    </div>
    <div class="container-fluid"> <!-- #4 -->
        {table1}
    </div>
    <div>情報提供:NHK</div> <!-- #5 -->
</body>
</html>

定義情報に追加したの以下の内容になります。
1. title:ページタイトル
2. h2:見出し + {date}
3. 4局の番組表table {table} -> table
4. 番組詳細table {table1} -> table1
5. クレジット表示

  • htmlファイル形式の保存
table = tv_table.to_html(classes=['table', 'table-bordered', 'table-hover'],escape=False)
table1 = detail_table.to_html(classes=['table', 'table-bordered', 'table-hover'],escape=False)
html = html_template.format(table=table, table1=table1, date=date)

with open('g1_table_' + str(date) + '.html', 'w') as f:
    f.write(html)

▼をクリックすると、ページ内リンクで番組詳細データにスクロールします。
スクリーンショット 2020-04-07 16.08.21.png
スクリーンショット 2020-04-07 16.08.48.png

参考サイト

まとめ

  • pandas.read_jsonpandas.to_htmlがどこまで使えるか確認したかったので、練習としては最適だった。
  • pandas.read_jsonは軽い入り口的な状態で終わったので、また材料を探そうと思う。
  • pandas.to_htmlは、そもそもの使い方が理解できたのが収穫でした。
  • ただBootstrapを導入しても自分のWeb力が低いので見てくれは残念な結果となった。
  • ニュースが気になる状況なので、朝一に実行しておけば、本家サイトの番組表に飛ばずに確認ができます。
  • NHK番組表API 利用上の注意を確認の上、用法・用量を守ってご利用ください。
3
6
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
6