ツイートをリアルタイムに検索し取得する (Ruby)
OAuth対応だとか、数年間でAPIの様式がコロコロ変わった印象のTwitter。
Rubyだとどう処理できるのか気になった。
Gem"Twitter" を使う
「特定のキーワードにマッチするツイートをリアルタイム取得する」方法。Twitterの検索は、データをリアルタイムに取得するAPIと、リアルタイムじゃないけど過去を含めて検索したりできるAPIの2通りがあって、今回はリアルタイム側を使う。
このモジュール1つで完結するのね…すげえ。
require "twitter" client = Twitter::Streaming::Client.new do |config| config.consumer_key = "" config.consumer_secret = "" config.access_token = "" config.access_token_secret = "" end keywords = "眠い,おやすみ" options = { :language => 'ja', :track => keywords } client.filter(options) do |object| if object.is_a?(Twitter::Tweet) printf "%s / %s\n %s\n\n", object.user.screen_name, object.user.name, object.text.gsub("\n", "\n ") end end
config部分はTwitterの開発者ページ*1の「Manage My Apps」からプログラム登録して設定する。
trackに指定しているキーワードは、カンマ区切りだとor検索、スペース区切りだとAND検索になる様子。その他の詳しいフィルタリングオプションは公式のAPI説明*2参照。
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つの場合も期待通りに動作する。
HTTP通信 (Ruby)
Rubyでスクレイピングをやりたくて、HTTP通信の方法から試行錯誤。
"net/http"を使う
最初に辿り着いた、ローレベルな実装方法。
PerlでいうところのLWP::UserAgentを使っている感じで凄く馴染み深い。
こちら*1を参考にさせて頂いて、少し手を加えた。
- GET/POST対応
- パラメータ渡せる
- 取得した文字列はバイナリ扱い(ASCII-8BIT)
Yahooのトップページを取得してみる
require 'net/http' require 'kconv' def http_request(method, uri, query_hash={}) query = query_hash.map{|k, v| "#{k}=#{v}"}.join('&') query_escaped = URI.escape(query) uri_parsed = URI.parse(URI.escape(uri)) # http = Net::HTTP.new(uri_parsed.host) case method.downcase! when 'get' return http.get(uri_parsed.path + '?' + query_escaped).body when 'post' return http.post(uri_parsed.path, query_escaped).body end end url = 'http://www.yahoo.co.jp' body = http_request('GET', url) puts body.toutf8
もっと簡潔に書く方法*2もあった。
文字コードはmetaタグのcharset属性値で判定するのが「行儀がいい」はずだが、判定した文字コードを元にUTF8に変換する適当な方法が見つけられなかったので、kconvでざっくり自動判定させた。
"open-uri"を使う
"net/http"のwrapperで、Kernel.openを拡張して直接URLがオープン出来るようになるGem。
引数がファイルパスでも開けるので注意が必要。*3
丁寧に使えばキャッシュ処理が透過的に実装できるから便利かもしれない。
- GETのみ?
- 取得した文字列はバイナリ扱い(ASCII-8BIT)
Yahooのトップページを取得してみる
require 'open-uri' require 'kconv' url = "http://www.yahoo.co.jp" body = open( URI.escape(url) ).read puts body.toutf8
リソースを取得するだけならこれが最短ぽい。
"mechanize"を使う*4
スクレイピングするなら"mechanize"が定番みたい。もの凄い高機能なGem。
- 勿論GET/POST対応
- パラメータも渡せる
- 受信したページは"nokogiri"がオブジェクト化してくれる
- ページオブジェクトは、Formに値をセットしてSubmit出来る
- ページオブジェクトは、XPathを使って解析できる
Yahooのトップからキーワード検索を行う
require 'mechanize' agent = Mechanize.new agent.user_agent_alias = 'Windows Mozilla' url = 'http://www.yahoo.co.jp' page1 = agent.get(url) form = page1.form_with(:name => 'sf1') form.field_with(:name => 'p').value = "デミオ" page2 = form.submit page2.search('//div[@class="w"]').each do |node| puts node.css('div > h3 > a').inner_text # ざっくり end
一覧ページから詳細ページURLを抽出、各ページに遷移して、その内容を解析…みたいな事がmechanizeだけで完結する。よく出来てるなあ。
Mechanizeは受信したHTMLをUTF8に変換し、以後UTF8で処理する。もしサーバ側にSJIS等でPOSTする必要がある場合はひと工夫必要、らしい。*5
Rubyの勉強に読ませて頂いた書籍
- 作者: 高橋征義,後藤裕蔵,まつもとゆきひろ
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2013/06/04
- メディア: 単行本
- この商品を含むブログ (30件) を見る
帯に書いてあった『本書は初心者の気持ちを裏切らない出来』というのは納得。
Rubyのコーディングで使う「基本用具一式」が把握できる本。
- 作者: Paolo Perrotta,角征典
- 出版社/メーカー: KADOKAWA/アスキー・メディアワークス
- 発売日: 2010/08/28
- メディア: 大型本
- 購入: 18人 クリック: 533回
- この商品を含むブログ (125件) を見る
という所から、
- クラスメソッドやインスタンスメソッド、シングルトンメソッドが定義される場所
- 継承とメソッド探索
- オープンオブジェクト
- 動的にインスタンス変数やインスタンスメソッドを生成(attr_*は作れる!)
- 変数スコープ
- オブジェクトのスコープでコードを実行する方法
などなど。
他言語に対するRubyのアドバンテージが見えてくる本。
- 作者: まつもとゆきひろ,David Flanagan,卜部昌平(監訳),長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2009/01/26
- メディア: 大型本
- 購入: 21人 クリック: 356回
- この商品を含むブログ (129件) を見る
個々に詳細なので、Rubyのアウトラインを理解してから読もうと思った。