第531話:テレビ番組表の自動検索

2017年4月17日

情報収集は「業務」か?

色々な意見がありそうです。テレビ番組も会議室で視聴すれば「業務」なのでしょうね。

ただ、たとえば百貨店に勤めている方なら「百貨店」のニュースは気になるものです。あるいはネット企業に勤めている方なら「AI」「IoT」「ビッグデータ」といったキーワードが気になるものです。

もし、それらのキーワードに関係する「特集番組」がテレビ放送として企画されているなら、見逃す訳には行きません。とくに公共放送で取り上げられるとなれば、社会への影響も非常に大きなものがあります。

<NHK API 管理画面>

誰でも使える NHK API

日本の公共放送であるNHKは、『番組表API』を公開しています。2016年6月にはVer.2の提供が開始され、「7日先」の番組情報まで取得できるようになりました。

レスポンス抜粋

"id" : "2017041215654",
"event_id" : "15654",
"start_time" : "2017-04-12T04:30:00+09:00",
"end_time" : "2017-04-12T05:00:00+09:00",
"title" : "NHKニュース おはよう日本",
"subtitle" : "▼国内外の最新ニュース ▼スポーツ情報 ▼地域の課題や話題のリポート ▼日本と世界の気象情報",
"content" : "※番組内容は変更になる場合があります ▼番組HP http://www.nhk.or.jp/ohayou/",
"act" : "【キャスター】森田洋平,【気象キャスター】酒井千佳",


この API は非常にシンプルな OPEN API です。

ユーザー登録さえすれば、誰でもスグにアクセスでき、利用上のルールに関しても「1日300アクセスまで」と非常に明快です。OAuth1/OAuth2といった認可フローもなく、セキュア通信(HTTPS)ですらありません!


一方で、標準仕様に厳格な API と言うこともできます。

日頃いろいろな API を活用している方であれば、Web サイトで使用できる文字(UTF-8)は、そのまま通信されるものと思い込んでしまっています。しかし、ソコは「流石NHK」です。「英数記号(ASCII 文字)だけで送受信」(Unicodeエスケープ)というルール(JSON RFC7159)をしっかり守り、日本語一文字一文字が \nXXXX と変換されています。(通信ログの検証等では使いづらいのですが)

[テレビ番組のキーワード検索]



「業務」の自動化

このワークフロー・アプリは、NHK API を通じて番組表情報を自動取得します。そして、あらかじめ設定しておいたキーワードにマッチする番組が無いかを自動探索する仕組みです。

たとえば「京都」というキーワードを設定しておけば、明日の朝4時に(!)「外国人向けの情報番組」があることをメールで確認できるようになります。いわゆる「ニュースアプリ」や『Google Alert』に代表されるような「Web 探索」とは違ったアプローチと言えます。

業務フローをよく見ればわかりますが、放置したとしても、24時間後に同じ条件で探索してくれます。

設定例

  • 地デジ総合: 京都,祇園,舞妓
  • 衛星BS1: 京都,祇園,舞妓,花街,先斗町,歌舞練場

通知メール抜粋

2017-04-14 04:11:00
◇ONE MINUTE ADVISER「関西空港から駅への行き方」
あの有名外国人スターが来日!?関空で駅までの行き方が分からないというスターにONE MINUTE ADVISERが楽しく解説します。京都・嵐山で外国人旅行者に人気の隠れスポットも紹介。

[テレビ番組のキーワード検索:「2.検索条件の変更」画面]

自動工程の設定方法

このワークフロー・アプリ(業務プロセス)のキモは、自動取得した番組表を検索するところです。

すなわち自動工程では「API通信でデータ取得する」だけでなく、「取得したデータから必要なデータを抽出する」という仕組みにしておく必要があります。もしプログラミングの知識がある方なら『スクリプト工程』でサーバサイド JavaScript を設定するのが良いでしょう。(そうでない方はスクリプトがパッケージ化された『サービス工程のAddonファイル』を活用してサービス工程の設定だけで済ませるべきです。)


『スクリプト工程』であれば、

キーワード「京都」に対して「東 "京都"」が検索ヒットする

といった悲劇にも対処することができます。つまり、「排除ワード」を設定できるようにしたり、「AND検索」を設定できる様にしたり、利用用途にあわせた様々な改良を楽しむことができるでしょう。(?)


ちなみに、クラウド型ワークフロー『Questetra BPM Suite』では、文字列型データの格納制限として「100万文字まで」というルールがあります。たとえば、地上波とBS放送のすべての番組表を一気に取得(nhkService="tv")して、文字列型データに格納してしまおうとすると、エラーになる可能性があるので注意が必要です。(←体験談)

<スクリプト工程「番組表取得」の設定例>
// TV Program search via NHK Program List API (ver. 20170410) 
// (c) 2017, Questetra, Inc. (the MIT License) 
// 情報提供: NHK 

//// == コンフィグ&参照 / Config & Retrieving == 
// あなたの "NHK番組表API" の "登録済みアプリ" 
// http://api-portal.nhk.or.jp/ 
var nhkKey = "Your-Key-32-Your-Key-32-Your-Key"; 

// http://api-portal.nhk.or.jp 
var nhkArea = data.get( "q_nhkArea" ) + ""; 
var nhkService = data.get( "q_nhkService" ) + ""; 
// 総合"g1",Eテレ"e1",BS1"s1",BSプレ"s3" 
var nhkDate = data.get( "q_nhkDate" ) + ""; 
var searchWordsCsv = data.get( "q_searchWordsCsv" ) + ""; 


//// == 演算 / Calculating == 
var accessLog = ""; 
var matchLog = ""; 

/// -- GET program list -- 
var uri = "http://api.nhk.or.jp/v2/pg/list/"; 
uri += nhkArea + "/"; 
uri += nhkService + "/"; 
uri += nhkDate + ".json?key="; 
uri += nhkKey; 

// com.questetra.bpms.core.event.scripttask.HttpClientWrapper 
// https://www.questetra.com/ja/tour/m2/m230/ 
// HttpRequestWrapper 
var requestMsg = httpClient.begin(); 

// HttpResponseWrapper 
var responseMsg = requestMsg.get( uri ); 
accessLog += "---GET--- " + responseMsg.getStatusCode() + " " + responseMsg.getCharset() + "\n"; 
accessLog += responseMsg.getResponseAsString() + "\n\n"; 

/// -- Word Search -- 
var responseObj = JSON.parse( responseMsg.getResponseAsString() ); 
var searchWords = searchWordsCsv.split(","); 

var tmp = ""; 
for (var i = 0; i < responseObj.list[nhkService].length; i ++) { // Bracket notation 
  tmp = responseObj.list[nhkService][i].start_time.slice(11, 16) + "\n"; 
  tmp += responseObj.list[nhkService][i].title + "\n"; 
  tmp += responseObj.list[nhkService][i].subtitle + "\n"; 
  tmp += responseObj.list[nhkService][i].content + " "; 
  tmp += responseObj.list[nhkService][i].act + "\n"; 

  for (var j = 0; j < searchWords.length; j ++) { 
    if ( tmp.match( searchWords[j] ) ) { 
      matchLog += tmp + "\n+++ \n\n"; 
      break; 
    } 
  } 
} 

//// == ワークフローデータへの代入 / Data Updating == 
retVal.put( "q_accessLog", accessLog ); 
retVal.put( "q_matchLog", matchLog ); 


<サービス工程Addonファイルの作成例>
<?xml version="1.0" encoding="UTF-8"?><service-task-definition> 

<label>NHK Program Search</label> 

<configs> 
  <config name="conf_DataIdA" required="true" form-type="TEXTFIELD"> 
    <label>A: Set NHK KEY</label> 
  </config> 
  <config name="conf_DataIdB" required="true" form-type="SELECT" select-data-type="STRING_TEXTFIELD"> 
    <label>B: Select AREA CODE</label> 
  </config> 
  <config name="conf_DataIdC" required="true" form-type="SELECT" select-data-type="STRING_TEXTFIELD"> 
    <label>C: Select SERVICE CODE</label> 
  </config> 
  <config name="conf_DataIdD" required="true" form-type="SELECT" select-data-type="DATE"> 
    <label>D: Select DATE</label> 
  </config> 
  <config name="conf_DataIdE" required="true" form-type="SELECT" select-data-type="STRING_TEXTFIELD"> 
    <label>E: Set SEARCH WORDS CSV</label> 
  </config> 
  <config name="conf_DataIdF" required="true" form-type="SELECT" select-data-type="TEXTAREA"> 
    <label>F: Select Access Log</label> 
  </config> 
  <config name="conf_DataIdG" required="true" form-type="SELECT" select-data-type="TEXTAREA"> 
    <label>G: Select Match Log</label> 
  </config> 
</configs> 


<script><![CDATA[ 
// TV Program search via NHK Program List API (ver. 20170411) 
// (c) 2017, Questetra, Inc. (the MIT License) 
// 情報提供: NHK 

//// == コンフィグ&参照 / Config & Retrieving == 
// あなたの "NHK番組表API" の "登録済みアプリ" 
// http://api-portal.nhk.or.jp/ 
var nhkKey = configs.get( "conf_DataIdA" ) + ""; 

// http://api-portal.nhk.or.jp
var nhkArea = data.get( configs.get( "conf_DataIdB" ) ) + "";
var nhkService = data.get( configs.get( "conf_DataIdC" ) ) + "";
 // 総合"g1",Eテレ"e1",BS1"s1",BSプレ"s3"
var nhkDate = data.get( configs.get( "conf_DataIdD" ) ) + "";
var searchWordsCsv = data.get( configs.get( "conf_DataIdE" ) ) + "";

var dataIdF = configs.get( "conf_DataIdF" );
var dataIdG = configs.get( "conf_DataIdG" );

//// == 演算 / Calculating == 
var accessLog = ""; 
var matchLog = ""; 

/// -- GET program list -- 
var uri = "http://api.nhk.or.jp/v2/pg/list/"; 
uri += nhkArea + "/"; 
uri += nhkService + "/"; 
uri += nhkDate + ".json?key="; 
uri += nhkKey; 

// com.questetra.bpms.core.event.scripttask.HttpClientWrapper 
// https://www.questetra.com/ja/tour/m2/m230/ 
// HttpRequestWrapper 
var requestMsg = httpClient.begin(); 

// HttpResponseWrapper 
var responseMsg = requestMsg.get( uri ); 
accessLog += "---GET--- " + responseMsg.getStatusCode() + " " + responseMsg.getCharset() + "\n"; 
accessLog += responseMsg.getResponseAsString() + "\n\n"; 

/// -- Word Search -- 
var responseObj = JSON.parse( responseMsg.getResponseAsString() ); 
var searchWords = searchWordsCsv.split(","); 

var tmp = ""; 
for (var i = 0; i < responseObj.list[nhkService].length; i ++) { // Bracket notation 
  tmp = responseObj.list[nhkService][i].start_time.slice(11, 16) + "\n"; 
  tmp += responseObj.list[nhkService][i].title + "\n"; 
  tmp += responseObj.list[nhkService][i].subtitle + "\n"; 
  tmp += responseObj.list[nhkService][i].content + " "; 
  tmp += responseObj.list[nhkService][i].act + "\n"; 

  for (var j = 0; j < searchWords.length; j ++) { 
    if ( tmp.match( searchWords[j] ) ) { 
      matchLog += tmp + "\n+++ \n\n"; 
      break; 
    } 
  } 
} 


//// == ワークフローデータへの代入 / Data Updating == 
retVal.put( dataIdF, accessLog ); 
retVal.put( dataIdG, matchLog ); 

]]></script> 
</service-task-definition> 


<SAMPLE RESPONCE> (※部分的に編集されています)
{
  "list":{
    "g1":[
      {
        "id" : "2017041219033",
        "event_id" : "19033",
        "start_time" : "2017-04-12T04:11:00+09:00",
        "end_time" : "2017-04-12T04:15:00+09:00",
        "area":{
          "id" : "260",
          "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" : "NHKプレマップ「連続テレビ小説 ひよっこ」",
        "subtitle" : "",
        "content" : "",
        "act" : "",
        "genres":[
          "0207"
        ]
      },
      {
        "id" : "2017041215654",
        "event_id" : "15654",
        "start_time" : "2017-04-12T04:30:00+09:00",
        "end_time" : "2017-04-12T05:00:00+09:00",
        "area":{
          "id" : "260",
          "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" : "NHKニュース おはよう日本",
        "subtitle" : "▼国内外の最新ニュース ▼スポーツ情報 ▼地域の課題や話題のリポート ▼日本と世界の気象情報",
        "content" : "※番組内容は変更になる場合があります ▼番組HP http://www.nhk.or.jp/ohayou/",
        "act" : "【キャスター】森田洋平,【気象キャスター】酒井千佳",
        "genres":[
          "0000",
          "0002",
          "0100"
        ]
      },
    ]
  }
}

<データ項目一覧画面>


[雛形ダウンロード (無料)]
<類似プロセス>
≪関連記事≫

[英文記事 (English Entry) ]

0 件のコメント :

コメントを投稿