# みんなどうやっているのだろう...
# この程度の処理は、ふつうのプログラマならさくっと自分で書いているのでしょうか。
モジュール名は(使い方が正しいのか微妙ですが)、RESTとしています。
require 'uri'
require 'cgi'
require 'open-uri'
require 'rexml/document'
require 'rexml/parsers/streamparser'
require 'rexml/parsers/baseparser'
require 'rss'
module REST
class RESTRequest
attr_reader :base_url
# base_url: WebAPIのリクエストURL
# params: リクエストパラメータ(ハッシュ)
# enc_params: URLエンコードが必要なリクエストパラメータ(ハッシュ)
def initialize(base_url, params, enc_params)
@base_url = base_url # WebAPI base URL
@req_params = Hash.new # request parameters
params.each{ |key, val|
set_param key, val
}
enc_params.each{ |key, val|
set_encoded_param key, val
}
@closed = false
@resp_str = nil
end
# リクエストURL作成
def make_request_url
url = @base_url + "?"
@req_params.each{|key, val|
url.concat "#{key}=#{val}&"
}
url.sub!(/&$/, "")
return url
end
# レスポンス文字列取得
def response_string
# リクエストを投げるのは初回のメソッド呼び出しのみ
if !@closed
begin
io = OpenURI::open_uri make_request_url
if io != nil
str = io.read
io.close
@resp_str = str # レスポンスのキャッシュ
end
rescue => ex
raise RESTRequestError, ex.message
end
@closed = true
end
return @resp_str
end
def set_param(key, val)
@req_params.store key, val
end
def set_encoded_param(key, val)
encoded_val = CGI::escape val
set_param key, encoded_val
end
private :set_param, :set_encoded_param
end
# 例外クラス
class RESTError < StandardError; end
class RESTRequestError < RESTError; end
class RESTResponseParseError < RESTError; end
# XMLレスポンスをパース
# req: RESTRequestオブジェクト
# listener: REXML::StreamListener を include した任意のリスナーオブジェクト
def parse_xml req, listener
xml = req.response_string
if xml != nil
begin
REXML::Parsers::StreamParser.new(xml, listener).parse
rescue => ex
raise RESTResponseParseError, ex.message
end
end
end
# RSSをパース
# req: RESTRequestオブジェクト
def parse_rss req
rss_str = req.response_string
if rss_str != nil
begin
rss = RSS::Parser.parse rss_str, false
return rss
rescue => ex
raise RESTResponseParseError, ex.message
end
end
end
module_function :parse_xml, :parse_rss
end
1つのクラスと2つの関数をもつモジュールです。
- RESTRequestクラス: リクエストURLの作成とレスポンス文字列の取得を受け持ちます。
- parse_xml関数: REXML::StreamListener を include した任意のリスナーオブジェクトを使ってレスポンスをパースします。
- parse_rss関数: RSS::Parser で RSS をパースします。
使いかた(かんたんバージョン)
Google Blog Search の RSS を取得してパースします。RSSなので parse_rss でほぼOKなのですが、RSSの仕様に含まれない<opensearch:totalResults>もとりたいので、その部分のみ parse_xml でとってきます。
google.rb
require 'rexml/streamlistener'
require 'rest'
include REST
class GoogleBlogResponseListener
include REXML::StreamListener
attr_reader :total_results
def tag_start(name, attrs)
@tag_name = name
end
def text text
if @tag_name == "opensearch:totalResults"
@total_results = text if @total_results == nil
end
end
end
base_url = "http://blogsearch.google.co.jp/blogsearch_feeds"
params = {
"hl" => "ja",
"lr" => "lang_ja",
"ie" => "utf-8",
"output" => "rss"
}
enc_params = {
"q" => "日食"
}
req = RESTRequest.new base_url, params, enc_params
puts req.make_request_url
listener = GoogleBlogResponseListener.new
begin
parse_xml req, listener
rss = parse_rss req
puts "Total Results: #{listener.total_results}"
if rss != nil
rss.items.each{ |item|
puts " - #{item.title} (#{item.dc_date.strftime '%Y/%m/%d-%X'})"
}
end
rescue RESTError => ex
puts "Error: #{ex.message}"
end
実行結果
$ ruby google.rb
http://blogsearch.google.co.jp/blogsearch_feeds?hl=ja&lr=lang_ja&ie=utf-8&output=rss&q=%E6%97%A5%E9%A3%9F
Total Results: 4416456
- 皆既<b>日食</b>: 星的考察。。。 (2009/07/23-19:01:43)
- 西内まりや Official Voice&Movie Blog:皆既<b>日食</b>!! (2009/07/22-21:33:45)
- 大萩康司のココ最近。 powered by ココログ: 皆既<b>日食</b> (2009/07/23-16:46:54)
- ねこの笹舟 <b>日食</b>! (2009/07/23-10:21:51)
- 130i を中心としたオハナシ : <b>日食</b>!! (2009/07/23-21:15:04)
- <b>日食</b>!:沖縄古着スクラップ [ロング ライフ プロジェクト] (2009/07/23-11:42:38)
- 皆既<b>日食</b>!|安田美沙子オフィシャルブログ「MICHAEL(ミチャエル <b>...</b> (2009/07/23-19:50:36)
- nana cafe : 皆既<b>日食</b> (2009/07/23-18:15:27)
- <b>日食</b>ゴーグル。|江戸むらさき 野村オフィシャルブログ『Men's クラブ <b>...</b> (2009/07/23-02:11:34)
- おおなかこなか: #2238 <b>日食</b>帯世界地図 (2009/07/23-06:36:04)
使いかた(モジュール組み込みバージョン)
上のコードをもう少しきれいに書きます。
具体的にはリクエストURLなどを隠蔽して、Google Blog Search専用の拡張モジュールを作成します。
require 'rexml/streamlistener'
require 'rest'
module GoogleBlogSearch
# Google Blog Searchのリクエストを隠蔽するクラス
class GoogleBlogRequest < REST::RESTRequest
BASE_URL = "http://blogsearch.google.co.jp/blogsearch_feeds"
def initialize query
params = {
"hl" => "ja",
"lr" => "lang_ja",
"ie" => "utf-8",
"output" => "rss"
}
enc_params = { "q" => query }
super BASE_URL, params, enc_params
end
end
class GoogleBlogResponseListener
include REXML::StreamListener
attr_reader :total_results
def tag_start(name, attrs)
@tag_name = name
end
def text text
if @tag_name == "opensearch:totalResults"
@total_results = text if @total_results == nil
end
end
end
# ブログ検索クラス
class BlogSearch
include REST # RESTモジュールを include
attr_reader :rss
def initialize query
@req = GoogleBlogRequest.new query
@listener = GoogleBlogResponseListener.new
end
def search
parse_xml @req, @listener
@rss = parse_rss @req
end
def total_results
@listener.total_results
end
end
end
# GoogleBlogSearch モジュールを使う
s = GoogleBlogSearch::BlogSearch.new "日食"
begin
s.search
puts "Total Results: #{s.total_results}"
s.rss.items.each{ |item|
puts " - #{item.title} (#{item.dc_date.strftime '%Y/%m/%d-%X'})"
}
rescue REST::RESTError => ex
puts "RESTError: #{ex.message}"
rescue => ex
puts "Error: #{ex.message}"
end
実行結果は上と同じになります。
0 件のコメント:
コメントを投稿