読者です 読者をやめる 読者になる 読者になる

Monthly Hacker's Blog

毎月のテーマに沿ったプログラミング記事を中心に書きます。

NHK番組表APIを使って番組情報を取得するCGIをpythonで作った

できること

この記事では、次のことができるようになります。

  • NHK番組表APIを使って任意の番組の番組表を取得する
  • APIで取得した情報を表示するCGIを作る*1

経緯

2016年6月から研究室でMonthly Hackという取り組みをはじめました。毎月テーマを決めて、メンバーで情報共有をしならハックをしようという取り組みです。

今月のテーマはスクレイピング。ということで、最初はクックパッドのスクレイピングにチャレンジしていました。しかし、こんなブログを見つけてしまいました。

techlife.cookpad.com

その中にはこのような記述が。

また、別の動機として、悪質なクロールを減らしたいというものがあります。

残念ながら、研究者の中には、クックパッドのデータを使用するため、悪質なクロールを行う方がいます。短時間に膨大なリクエストが送られると、最悪の場合、クックパッドのサービスに影響が出る恐れがあります。

どうやら(当たり前ですが)クロールおよびスクレイピングはしないで欲しいようです。そこでクックパッド以外のサイトでスクレイピングをしようと考え、思いついたのが今日の2355と明日の0655の番組表をスクレイピングで取得することです。みなさん、2355はご存知ですか?

NHKオンライン | Eテレ 2355

一部で熱狂的な?ファンがいる番組です。僕もその1人です。日中は、「今日の2355はどんな内容かなー」と思いを馳せているので、いっそのこといつでも確認できるようにしようと思い、番組表をスクレイピングしてくることを考えました。これなら何度もサーバーにアクセスする必要のない目的なので、安心して利用できます。2355の素敵な曲が聴けるCDはこちらから。

0655/2355 ソングBest!  明日がくるのをお知らせします

0655/2355 ソングBest! 明日がくるのをお知らせします

2355/0655 ソングBest!

2355/0655 ソングBest!



下調べの過程で気付いたのですが、なんとNHKは番組表APIを公開しているのです。そこで今回はMonthly Hackの趣旨からずれますが、APIを利用することにしました。

NHK番組表APIのユーザー登録

NHK番組表API
登録はこちらから。ユーザー登録自体は難しくもないと思いますので、「アプリを編集」の解説をします。

App name

必須項目です。お好きな名前をつけて登録してください。

アプリのURL

任意項目です。サーバー上で公開する場合はそのURLを登録してください。

プロダクト

必須項目です。NHK Program Guide APIを選んでください。

保存したらMy Appsが表示されるので、今回保存した名前をクリックし、APIキー(英数字32文字)を控えておいてください。このAPIキーによってユーザーを認識しています。

コード

実装したコードは、次のようなアルゴリズムです。

  • 日付を取得する
  • サーバー上のキャッシュファイルを読み込む
  • キャッシュファイルに今日の情報があれば印字する
  • キャッシュファイルに今日の情報がなければAPIを呼び印字する
  • キャッシュファイルを更新する

今回は大学の公開サーバーを利用しています。*2
pythonは2.7、パスは/usr/bin/pythonです。

コードはこちらです。*3

#!/usr/bin/python
# coding: utf-8
import urllib2
import json
import datetime
import random
import csv
import os


# define function
# convert datetime type to str type
def date2str(date):
    year = str(date.year)
    month = str(date.month).zfill(2)
    day = str(date.day).zfill(2)
    return [year, month, day]


# read csv file and return list
def reader(file, delimiter=','):
    f = open(file, 'r')
    read = csv.reader(f, delimiter=delimiter)
    data = []
    for x in read:
        data.append(x)
    f.close()
    return data


# save list as csv file
def writer(file, data, type='w+'):
    f = open(file, type)
    writer = csv.writer(f)
    writer.writerows(data)
    f.close()

# setting date
today = datetime.date.today()
tomorrow = today + datetime.timedelta(days=1)
today, tomorrow = date2str(today), date2str(tomorrow)

# data
# [year, month, day, title, subtitle, content, act]
dirname = os.path.abspath(os.path.dirname(__file__))
try:
    list2355 = reader(dirname + '/2355.csv')
except:
    list2355 = []

try:
    list0655 = reader(dirname + '/0655.csv')
except:
    list0655 = []


# api_key
api_key = 'API_KEY'

# api
today_url = 'http://api.nhk.or.jp/v2/pg/list/130/e1/{}.json?key={}'.format(
    '-'.join(today), api_key)
tomorrow_url = 'http://api.nhk.or.jp/v2/pg/list/130/e1/{}.json?key={}'.format(
    '-'.join(tomorrow), api_key)


# display
print '''
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=10.0, user-scalable=yes">
<title>今日の2355明日の0655</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link rel="stylesheet" href="sample.css">
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script src="//css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script>
<![endif]-->
</head>
<body>
'''

if list2355[-1][0:3] == today:
    print '今日の'
    for text in list2355[-1][3:7]:
        print text.replace('トビー', '<font color="blue">トビー</font>'), '<br />'
else:
    today_programs = json.loads(
        urllib2.urlopen(today_url).read())['list']['e1']
    for program in today_programs:
        if u'Eテレ2355' in program['title']:
            print '今日の', program['title'].encode('utf-8'), '<br />'
            print program['subtitle'].encode('utf-8'), '<br />'
            print program['content'].encode('utf-8').replace('トビー', '<font color="blue">トビー</font>'), '<br />'
            print program['act'].encode('utf-8'), '<br />'
            list2355.append([today[0], today[1], today[2],
                            program['title'].encode('utf-8'),
                            program['subtitle'].encode('utf-8'),
                            program['content'].encode('utf-8'),
                            program['act'].encode('utf-8')])

print '<br />'
print '<br />'
print '<br />'

if list0655[-1][0:3] == tomorrow:
    print '明日の'
    for text in list0655[-1][3:7]:
        print text.replace('トビー', '<font color="blue">トビー</font>'), '<br />'
else:
    tomorrow_programs = json.loads(
        urllib2.urlopen(tomorrow_url).read())['list']['e1']
    for program in tomorrow_programs:
        if u'Eテレ0655' in program['title']:
            print '明日の', program['title'].encode('utf-8'), '<br />'
            print program['subtitle'].encode('utf-8'), '<br />'
            print program['content'].encode('utf-8').replace('トビー', '<font color="blue">トビー</font>'), '<br />'
            print program['act'].encode('utf-8'), '<br />'
            list0655.append([tomorrow[0], tomorrow[1], tomorrow[2],
                            program['title'].encode('utf-8'),
                            program['subtitle'].encode('utf-8'),
                            program['content'].encode('utf-8'),
                            program['act'].encode('utf-8')])

print '<br />'
print '<br />'
print '<br />'
print '<br />'
print '<br />'
print '<br />'
print '情報提供:NHK'
# print 'NHK番組の情報提供:NHK'
print '''
</body>
</html>
'''
writer(dirname + '/2355.csv', list2355)
writer(dirname + '/0655.csv', list0655)

フロントエンドに強い人であればhtmlとCGIの2ファイルに分けて書けるのかもしれませんが、1ヶ月ではそこまでクオリティをあげられなかったです。このあと、キモであるAPIの部分を少し解説していこうと思います。
なお、NHK番組表APIを利用するときは'情報提供:NHK'または'NHK番組の情報提供:NHK'と必ず記載する必要があります。個人的には全角英字が気になって仕方ありません。

NHK番組表APIの使い方

使い方は非常に簡単です。まずNHK番組表APIのサイトにアクセスし、利用したいAPIをクリックします。その後、Resource URLの赤字部分を必要に応じて書き換えれば準備完了です。この作業に該当するのが、上記のコードでいうところの次の部分です。

# api
today_url = 'http://api.nhk.or.jp/v2/pg/list/130/e1/{}.json?key={}'.format(
    '-'.join(today), api_key)
tomorrow_url = 'http://api.nhk.or.jp/v2/pg/list/130/e1/{}.json?key={}'.format(
    '-'.join(tomorrow), api_key)

このURLにアクセスすると、json形式で情報を取得することができます。次の部分です。

urllib2.urlopen(today_url).read()

これはjson形式なので、pythonでjsonを扱うためのパッケージを利用します。次の部分です。

today_programs = json.loads(urllib2.urlopen(today_url).read())['list']['e1']

これでtoday_programsに今日の番組一覧が入ります。他のNHK番組表APIの場合もurllib2(python3.xの場合はurllib)とjsonの組み合わせで簡単に情報を抜き出すことができます。

最後に

7月のテーマはBotになりました。しかし、さっそくネタ切れ気味なので8月のテーマがなかなか決まりません。個人的にはマイコン工作したいけど、お金が掛かるので難しいなーと。

*1:Pythonが動くサーバーでないと利用できません

*2:http://web.sfc.keio.ac.jp/~p11771dh/nhk/main.cgi

*3:API_KEYのところをご自身のAPI_KEYに変えてお使いください