前回、NHKのNow On Air情報(以下NOA情報)のURLにアクセスすれば、「らじる★らじる」で放送している番組のタイトルや曲目情報等がJSON形式で入手できることを紹介しました。
入手できる情報は番組によって異なるものの、たいていの音楽番組では放送した曲目一覧を提供しているので、それらをファイル名と紐づけて記録しておけば、聞きたい番組や曲を探す時に便利でしょう。そう考えて、NOA情報から得た番組情報をデータベース化するスクリプトを書いてみました。
番組情報データベースの設計
かってエア・チェックを趣味にしていた人間としては、前回紹介した専門誌の番組表のように、出演者や曲目を簡単に一覧できることが理想です。
そのため番組情報をテキストベースで集積していこうかとも考えたものの、多数の曲目リストを収めなければならない音楽番組と、せいぜい出演者名しか不要な語学番組を1つの表に収めるのも見通しが悪そうです。
そこで項目数を固定にした番組名のリストと任意数の曲目を登録できる内容リストは別の表として整理することにしました。表(テーブル)形式のデータを操作するなら、SQLで利用できるデータベースの形にしてしまうのが簡単そうです。
前回の最後に、NOAから得られる番組情報の一覧を示しました。これらのうち、indexやhashtag、linkやrebroad等の項目は特に保存しておく必要はなさそうです。また、subtitleやcontentとして登録されている情報はfreeの一部を取り出しているだけなので、freeを記録するようにすれば不要でしょう。そのあたりを考慮して、番組名リストと内容リストは以下のような形式にしました。
内容リスト
番組ID | 曲名 1 |
番組ID | 曲名 2 |
番組ID | 曲名 3 |
… | … |
前者の番組名リストは1つの番組を1行に記録し、各番組には一意のID番号を振ります。一方、後者の内容リストは、free行に記録された曲目等の情報が、番組名リストのIDに紐づけられて任意の数だけ並んでいく、というシンプルな構造です。
だいたいのアイデアがまとまったので、これらの操作を使いなれたPythonとSQLite3で書いてみることにしました。
DB登録用スクリプト
まずデータベースを作成(初期化)する部分を作ります。SQLite3の場合、データベースは1つのファイルに保存されるので、使用するファイル名を引数として受けとることにしました。その中に含まれる番組名リストはtitlesというテーブル名、内容リストはcontentsという名前にしました。
create tableしている部分を見ると気づくように、番組名リスト(titlesテーブル)の定義の中に先に示した「番組ID」はありません。これはSQLite3の場合、各行には自動的にROWIDと呼ばれるID番号が振られるので、このROWIDを番組IDに流用すればいいだろう、と判断したためです。
次に、作成したテーブルにデータを登録する部分を作りました。テーブルはtitlesとcontentsに分かれているので、登録用の関数も2つに分けています。Pythonの場合、複数のデータをひとかたまりにしたタプルと呼ばれるデータ構造が利用できるので、登録すべきデータはタプル(t)で渡し、書き込み先のデータベースはカーソルオブジェクト(cursor)で渡しています。タプル内のデータは、スクリプト中に"?"で指定したプレースホルダーに展開されるため、テーブルの構造に従ったタプルはこの関数を呼び出す側で用意することにして、登録用関数では最低限の処理に留めています。
contentsテーブルにデータを登録する関数もほぼ同じですが、登録すべき項目は番組IDと曲目だけなので、登録するデータは2つだけです。
次に、前回紹介した操作を元に、JSON形式でNOA情報を取り込む部分を作りました。urllib2.urlopen()は指定したURLを開くための関数で、インターネット上のURLをファイルと同じように操作できるオブジェクトを返します。このオブジェクトにread()メソッドを適用し、必要なデータを読み出します。
前回紹介したように、NOAサイトから読み出したデータには冒頭と末尾に余計なデータが付いているので、それらをはぎとって正式なJSON形式にしてからjson.loads()で読みこみ、Pythonの辞書型データとして返します。
NOA情報にはラジオ第一、第二、FMの3チャンネル分の番組情報が含まれているので、必要なデータを取り出す際にはどのチャンネルのデータを使うかを決める必要があります。また、データベースに登録するファイル名も外部から与える必要があるので、このスクリプトでは引数としてチャンネルとファイル名を受けとることにしました。
最初の引数で指定するchannelはfm、r1、 r2の3択で、それぞれをNOA情報のチャンネル名に変換しています。また、必要なのは現在放送中の番組情報のみなので、末尾のインデックスは0に揃えました。
データベースと接続する部分はこんな感じにしてみました。データベースファイルは~/MP3/radiru_titles.sql3とし、このファイルが無ければ前述の初期化処理でデータベースを作成し、ファイルがあればデータベースとして接続します。
あとは実際にNOA情報を読み込み、指定したチャンネルの情報を取り出して、データベースに記録する作業です。
まずは get_json_data() を使ってNOA情報を取り込み、指定したチャンネル(ch)の情報から、「番組タイトル(title)」「出演者(act)」「録音日時(date)」の情報を取り出します。
前回も見たように、JSON形式で使用する文字コードはUTF-8で、SQLite3も保存するテキストデータの文字コードもUTF-8なので、JSON形式から切り出したデータはそのまま(文字コード変換なしに)記録することができます。
一方、コマンドラインから引数として渡されるファイル名はロケールで指定された文字コードになるため、SQLite3に記録するためには文字コードを変換する必要があります。
書き込むべきデータが揃えば、それらをinsert_title()に渡してデータベースに書き込んだ後、書き込んだ行のROWIDを取り出しておきます。
次に、番組情報のうちfree行の部分を取り出して、改行記号が2つ並ぶ(=1行空け)ごとに分割して、それぞれをtitlesテーブルのROWIDと紐づけて、contentsテーブルに書きこんで行きます。
動作確認
以上、紹介したリストを1つにまとめ、モジュールのimport処理やmain()関数の呼び出し処理を付けたスクリプト全体をリスト1に示します。なおリスト1には、前節では紹介を省いたcheck_char()の処理と共に、デバッグ用に情報をプリントアウトする処理もいくつか追加しています。
このスクリプトを実際に動かすと、このような動作になります。
radiru_noa.pyスクリプトが記録するSQLite3形式のデータベースはsqlite3コマンドを用いて直接眺めることもできるので、データベースにどのようなデータが登録されたのかを調べてみましょう。
なお、Plamo Linuxの場合、デフォルトの文字コードはEUC-JPになっていて、そのままではsqlite3コマンドの出力が文字化けするので、端末の文字コードをUTF-8に変更して操作する必要があります。
無事、NOA情報がデータベースファイルに記録されているようです。
以前紹介したように、手元では「らじる★らじる」のタイマー録音は、radiru_rec.pyスクリプトが生成するシェルスクリプトを、atコマンドで指定した時刻に実行することで実現しています。そのため、radiru_rec.pyスクリプトが生成するシェルスクリプトに、今回作成したradiru_noa.pyスクリプトを呼び出す処理を追加すれば、録音した番組の情報をデータベースに自動的に追加していくことができます。まずはこうしてタイマー録音した番組の情報をデータベースに記録するようにしてみました。