動機
- ネタ探しをしていて、Qiita.com:個人でも使える!おすすめAPI一覧の投稿に出会う。
- コメント欄に「NHK 番組表 APIもいいよ」とあったので覗いて見たら簡単そうだった。
- REST APIから取得(JSON形式)して、加工・整形してHTML作成まではいい練習と思い。
- NHK番組APIを使ってオリジナルの番組表を作って見ました。(見てくれは残念ですが、、、)
API keyの取得の流れ
- NHK番組APIで、アカウント登録を行います。
- メール認証を経て、アプリケーション名:(好きな名称)、URL:(未登録)を登録します。
- 登録が完了すると
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> 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>
定義情報に追加したの以下の内容になります。
- title:ページタイトル
- h2:見出し + {date}
- 4局の番組表table {table} -> table
- 番組詳細table {table1} -> table1
- クレジット表示
- 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)
▼をクリックすると、ページ内リンクで番組詳細データにスクロールします。
参考サイト
- pandasのDataFrameを元に、画像入りの表をつくる
- 上記のサイト情報から bootstrapのバージョンを最新化しました。
まとめ
-
pandas.read_json
とpandas.to_html
がどこまで使えるか確認したかったので、練習としては最適だった。 -
pandas.read_json
は軽い入り口的な状態で終わったので、また材料を探そうと思う。 -
pandas.to_html
は、そもそもの使い方が理解できたのが収穫でした。 - ただ
Bootstrap
を導入しても自分のWeb力が低いので見てくれは残念な結果となった。 - ニュースが気になる状況なので、朝一に実行しておけば、本家サイトの番組表に飛ばずに確認ができます。
- NHK番組表API 利用上の注意を確認の上、用法・用量を守ってご利用ください。