XMLをParseする (Ruby)
Radikoの番組表データを取得する
東京エリアの放送内容をXMLで取得し、色んなモジュールでパースしてみる。
APIの仕様はこちらのページ*1を参考にさせて頂いた。
APIと応答されるXML内容
地域別番組表API
http://radiko.jp/v2/api/program/today?area_id=[area_id]
エリアコードを指定してURLにアクセスすると、その地域の全放送局の放送内容がXMLで返される。
東京エリア(JP13)を指定すると以下のXMLが応答された。
<?xml version="1.0" encoding="UTF-8"?> <radiko> <ttl>1800</ttl> <srvtime>1433606138</srvtime> <stations> <station id="TBS"> <name>TBSラジオ</name> <scd> <progs> <date>20150606</date> <prog ft="20150606050000" to="20150606050500" ftl="0500" tol="0505" dur="300"> <title>ニュース・天気予報</title> <sub_title /> <pfm></pfm> <desc /> <info><img src='http://www.tbs.co.jp/radio/todays954/photo/noimage.gif'><br /><br /></info> <metas> <meta name="twitter" value="#radiko" /> <meta name="facebook-fanpage" value="http://www.facebook.com/radiko.jp" /> <meta name="twitter-hash" value="#radiko" /> </metas> <url>http://www.tbs.co.jp/radio/</url> </prog> <prog ft="20150606050500" to="20150606060000" ftl="0505" tol="0600" dur="3300"> <title>生島ヒロシのサタデー・一直線</title> <sub_title /> <pfm>生島ヒロシ/寺田理恵子ゲスト:舟木一夫/森山良子</pfm> <desc /> <info><img src='http://www.tbs.co.jp/radio/todays954/photo/sat-1.jpg'><br /><br />先週に引き続き、生島ヒロシさんが憧れている舟木一夫さんが登場。<br/>「サタイチ週末大人クラブ」では、森山良子さんとの対談をお届け。<br/><br/>メール:<a href="mailto:sat-1@tbs.co.jp">sat-1@tbs.co.jp</a><br/></info> <metas> <meta name="twitter" value="#radiko" /> <meta name="twitter-hash" value="#radiko" /> <meta name="facebook-fanpage" value="http://www.facebook.com/radiko.jp" /> </metas> <url>http://www.tbs.co.jp/radio/sat-1/</url> </prog> </progs> </scd> </station> </stations> </radiko>
(抜粋。本当はstationノードもprogノードもたくさんある)
"REXML"を使ってパース
require 'open-uri' require 'kconv' require 'rexml/document' url = 'http://radiko.jp/v2/api/program/today?area_id=JP13' xml = open( url ).read.toutf8 doc = REXML::Document.new(xml) doc.elements.each('radiko/stations/station') do |station| printf "---%s (%s)---\n", station.attributes['id'], station.elements['name'].text station.elements.each('scd/progs/prog') do |prog| printf "%d-%d(%2d) : %s(%s)\n", prog.attributes['ft'], prog.attributes['to'], prog.attributes['dur'].to_i/60, prog.elements['title'].text, prog.elements['pfm'].text end end
凄くstrictな感じ。見やすい。
"ActiveSupport::XMLConverter"を使ってパース
RubyOnRailsに付属してるライブラリ。
require 'open-uri' require 'kconv' require 'active_support/core_ext/hash/conversions' url = 'http://radiko.jp/v2/api/program/today?area_id=JP13' xml = open( url ).read.toutf8 hash = Hash.from_xml(xml) hash['radiko']['stations']['station'].each do |station| printf "---%s (%s)---\n", station['id'], station['name'] station['scd']['progs']['prog'].each do |prog| printf "%d-%d(%2d) : %s(%s)\n", prog['ft'], prog['to'], prog['dur'].to_i/60, prog['title'], prog['pfm'] end end
特定ノードの「属性値」と、そのノードの「子ノード」の区別が無くなる。まあこれは大した問題じゃない。
問題なのは、対象ノードが「1つの場合」と「複数の場合」とで格納内容が変わってしまう点。例えば上記に貼った「抜粋版」のXMLだと、stationノードは1つだけなので配列になっておらず、このコードでは動作しない。
こういった点を考慮するとコードが膨らむので、「要素数が変化する外部サービスのXML」を処理する場合は使わないほうが良さそう。"REXML"のeachは対象ノードが1つの場合も期待通りに動作する。