NHKラジオニュースサイトの音声ファイルのありか

最近 Amazon Echo Dot を入手して遊んでいる @riocampos です。

Amazon Echoでニュースを聞く

Echo Dot へ向かって「Alexa、ニュース」と言うと NHK ラジオニュースの最新ニュースを流し、そのあとに天気予報を喋ってくれます。便利です。
さて、そのニュース元である NHK ラジオニュースの音声ファイルはどこにあるのかを確認してみました。

音声ファイルの場所を示すファイル

https://api.nhk.or.jp/r-news/v1/newslist.js
です。中身を見てみましょう。

radionews(
  {"lastBuildDate":"Fri, 02 Feb 2018 15:00:07 +09:00",
    "news":[
      {
        "startdate":"Thu, 01 Feb 2018 19:00:03 +09:00",
        "enddate":"Thu, 01 Feb 2018 19:30:00 +09:00",
        "title":"夜7時NHKきょうのニュース",
        "soundlist":{
          "sound_normal":{"size":"14376717","type":"mp3","duration":"1797","filename":"20180201190003_16134_1_1_1"},
          "sound_fast":{"size":"8631693","type":"mp3","duration":"1078","filename":"20180201190003_16134_2_1_1"},
          "sound_slow":{"size":"17263053","type":"mp3","duration":"2157","filename":"20180201190003_16134_3_1_2"}
        }
      },
      {
        "startdate":"Thu, 01 Feb 2018 22:00:03 +09:00",
        "enddate":"Thu, 01 Feb 2018 23:10:00 +09:00",
        "title":"夜10時NHKジャーナル",
        "soundlist":{
          "sound_normal":{"size":"16788717","type":"mp3","duration":"4197","filename":"20180201220003_16379_1_1_2"},
          "sound_fast":{"size":"20152269","type":"mp3","duration":"2518","filename":"20180201220003_16379_2_1_1"},
          "sound_slow":{"size":"20152125","type":"mp3","duration":"5037","filename":"20180201220003_16379_3_1_3"}
        }
      },
      {
        "startdate":"Fri, 02 Feb 2018 07:00:03 +09:00",
        "enddate":"Fri, 02 Feb 2018 07:20:00 +09:00",
        "title":"朝7時NHKけさのニュース",
        "soundlist":{
          "sound_normal":{"size":"9577485","type":"mp3","duration":"1197","filename":"20180202070003_15388_1_1_1"},
          "sound_fast":{"size":"5752269","type":"mp3","duration":"718","filename":"20180202070003_15388_2_1_2"},
          "sound_slow":{"size":"11504205","type":"mp3","duration":"1437","filename":"20180202070003_15388_3_1_3"}
        }
      },
      {
        "startdate":"Fri, 02 Feb 2018 08:00:03 +09:00",
        "enddate":"Fri, 02 Feb 2018 08:05:00 +09:00",
        "title":"午前8時のNHKニュース",
        "soundlist":{
          "sound_normal":{"size":"2376621","type":"mp3","duration":"297","filename":"20180202080003_13346_1_1_1"},
          "sound_fast":{"size":"1431693","type":"mp3","duration":"178","filename":"20180202080003_13346_2_1_2"},
          "sound_slow":{"size":"2863341","type":"mp3","duration":"357","filename":"20180202080003_13346_3_1_3"}
        }
      },
      {
        "startdate":"Fri, 02 Feb 2018 12:00:03 +09:00",
        "enddate":"Fri, 02 Feb 2018 12:15:00 +09:00",
        "title":"正午のNHKニュース",
        "soundlist":{
          "sound_normal":{"size":"7177293","type":"mp3","duration":"897","filename":"20180202120003_15633_1_1_1"},
          "sound_fast":{"size":"4312269","type":"mp3","duration":"538","filename":"20180202120003_15633_2_1_1"},
          "sound_slow":{"size":"8623917","type":"mp3","duration":"1077","filename":"20180202120003_15633_3_1_2"}
        }
      }
    ]
  }
);

ニュースのスピードは「ふつう・ゆっくり・はやい」の三種類から選べるので、音声ファイルもそれぞれ3種類あります。
例えば「正午のNHKニュース」のふつうスピードの音声ファイルへのリンクは
http://www.nhk.or.jp/r-news/ondemand/mp3/20180202120003_15633_1_1_1.mp3?201802025155441
のような形式になっています。"正午のNHKニュース"のところの"filename"要素は"20180202120003_15633_1_1_1"になっているので、つまり

http://www.nhk.or.jp/r-news/ondemand/mp3/

に目的の"filename"要素+".mp3"としてしまえば、音声ファイルの URL が得られますね。
なお、この URL の"?"以降ですが、どうやらアクセスした日時 YYYY/MM/DD HH:MM:SS に対して

?YYYYMMDD5HHMMSS

となっているようです。他の日にセパレータ"5"が変わっていないかどうかをチェックしたいと思います*1

Rubyで音声ファイルURLを取り出す

中身はほぼ JSON なのですが、JSON じゃなく JavaScript ファイルになっています。っていうか JSONP って言うんでしたっけ、よく分かりません。
このため、そのままでは JavaScript 以外だと取り扱いづらくなっています。JSON にしてしまいましょう。
いつものように Ruby で取り扱います。
JavaScript 的には radionews 関数として読めるようになっているので、その内側の部分を取得するようにしてしまえば JSONになりますね。

> require 'json'
=> true
> require 'open-uri'
=> true
> js = open('https://api.nhk.or.jp/r-news/v1/newslist.js').read;

> json = js[/radionews\(([^)]+)\)/, 1];

> hash = JSON.parse(json)
=> {"lastBuildDate"=>"Fri, 02 Feb 2018 15:00:07 +09:00",
 "news"=>
  [{"startdate"=>"Thu, 01 Feb 2018 19:00:03 +09:00",
    "enddate"=>"Thu, 01 Feb 2018 19:30:00 +09:00",
    "title"=>"夜7時NHKきょうのニュース",
    "soundlist"=>
     {"sound_normal"=>{"size"=>"14376717", "type"=>"mp3", "duration"=>"1797", "filename"=>"20180201190003_16134_1_1_1"},
      "sound_fast"=>{"size"=>"8631693", "type"=>"mp3", "duration"=>"1078", "filename"=>"20180201190003_16134_2_1_1"},
      "sound_slow"=>{"size"=>"17263053", "type"=>"mp3", "duration"=>"2157", "filename"=>"20180201190003_16134_3_1_2"}}},
   {"startdate"=>"Thu, 01 Feb 2018 22:00:03 +09:00",
    "enddate"=>"Thu, 01 Feb 2018 23:10:00 +09:00",
    "title"=>"夜10時NHKジャーナル",
    "soundlist"=>
     {"sound_normal"=>{"size"=>"16788717", "type"=>"mp3", "duration"=>"4197", "filename"=>"20180201220003_16379_1_1_2"},
      "sound_fast"=>{"size"=>"20152269", "type"=>"mp3", "duration"=>"2518", "filename"=>"20180201220003_16379_2_1_1"},
      "sound_slow"=>{"size"=>"20152125", "type"=>"mp3", "duration"=>"5037", "filename"=>"20180201220003_16379_3_1_3"}}},
   {"startdate"=>"Fri, 02 Feb 2018 07:00:03 +09:00",
    "enddate"=>"Fri, 02 Feb 2018 07:20:00 +09:00",
    "title"=>"朝7時NHKけさのニュース",
    "soundlist"=>
     {"sound_normal"=>{"size"=>"9577485", "type"=>"mp3", "duration"=>"1197", "filename"=>"20180202070003_15388_1_1_1"},
      "sound_fast"=>{"size"=>"5752269", "type"=>"mp3", "duration"=>"718", "filename"=>"20180202070003_15388_2_1_2"},
      "sound_slow"=>{"size"=>"11504205", "type"=>"mp3", "duration"=>"1437", "filename"=>"20180202070003_15388_3_1_3"}}},
   {"startdate"=>"Fri, 02 Feb 2018 08:00:03 +09:00",
    "enddate"=>"Fri, 02 Feb 2018 08:05:00 +09:00",
    "title"=>"午前8時のNHKニュース",
    "soundlist"=>
     {"sound_normal"=>{"size"=>"2376621", "type"=>"mp3", "duration"=>"297", "filename"=>"20180202080003_13346_1_1_1"},
      "sound_fast"=>{"size"=>"1431693", "type"=>"mp3", "duration"=>"178", "filename"=>"20180202080003_13346_2_1_2"},
      "sound_slow"=>{"size"=>"2863341", "type"=>"mp3", "duration"=>"357", "filename"=>"20180202080003_13346_3_1_3"}}},
   {"startdate"=>"Fri, 02 Feb 2018 12:00:03 +09:00",
    "enddate"=>"Fri, 02 Feb 2018 12:15:00 +09:00",
    "title"=>"正午のNHKニュース",
    "soundlist"=>
     {"sound_normal"=>{"size"=>"7177293", "type"=>"mp3", "duration"=>"897", "filename"=>"20180202120003_15633_1_1_1"},
      "sound_fast"=>{"size"=>"4312269", "type"=>"mp3", "duration"=>"538", "filename"=>"20180202120003_15633_2_1_1"},
      "sound_slow"=>{"size"=>"8623917", "type"=>"mp3", "duration"=>"1077", "filename"=>"20180202120003_15633_3_1_2"}}}]}

目的の hash が得られました。

正午のニュースの音声ファイルURLを取得

まずは正午のニュースのハッシュ要素 noon を得ましょう。

> noon = hash["news"].find { |item| item["title"]["正午"] }                                                          
=> {"startdate"=>"Fri, 02 Feb 2018 12:00:03 +09:00",
 "enddate"=>"Fri, 02 Feb 2018 12:15:00 +09:00",
 "title"=>"正午のNHKニュース",
 "soundlist"=>
  {"sound_normal"=>{"size"=>"7177293", "type"=>"mp3", "duration"=>"897", "filename"=>"20180202120003_15633_1_1_1"},
   "sound_fast"=>{"size"=>"4312269", "type"=>"mp3", "duration"=>"538", "filename"=>"20180202120003_15633_2_1_1"},
   "sound_slow"=>{"size"=>"8623917", "type"=>"mp3", "duration"=>"1077", "filename"=>"20180202120003_15633_3_1_2"}}}

定時ニュースは5分間ですが、けさのニュースは20分、正午のニュースは15分、(夜7時の)きょうのニュースは30分、NHK ジャーナルは10分間なので、これらのニュースを聞くことが多いかと思います。それぞれ"けさ"、"正午"、"きょう"、"ジャーナル"を Array#find のピックアップ要素にしてやれば抜き出せますね。

続いて通常スピードの音声ファイルの"filename"要素を得ましょう。

> normal_speed_file = noon["soundlist"]["sound_normal"]["filename"]                                          
=> "20180202120003_15633_1_1_1"

では音声ファイルのURLを出してみましょう。

> base_url = "http://www.nhk.or.jp/r-news/ondemand/mp3/";
> mp3 = ".mp3";
> url = base_url + normal_speed_file + mp3                                                                   
=> "http://www.nhk.or.jp/r-news/ondemand/mp3/20180202120003_15633_1_1_1.mp3"

これで目的の URL が得られました。めでたしめでたし。

*1:2/2は5、2/4は0