RSSを読み込む:rubygems:simple-rss

結局,夏期休暇の課題をメーラでやるのは間に合いそうにないんで,RSSリーダーをでっち上げて提出したのですが,そのRSSフィードをパースする部分はrexml/documentでもってやったわけです.
しかしながら先人ってのは色んな分野に居るもので,gemを検索して見ると出てきたよ!

  • simple-rss (1.1, 1.0.0)

A simple, flexible, extensible, and liberal RSS and Atom reader for Ruby. It is designed to be backwards compatible with the standard RSS parser, but will never do RSS generation.

早速試してみるぞ.長くなるので『続きを読む』で色々する.

ソースをチラッと読む

なんかスゲー短い.simple-rss.rbだけで,他にライブラリやモジュール的なものは一切無し.たったの131行で,rexmlとか使うのかなーとか思ったら,requireしてるのはcgiとtimeだけ.何故にcgi.コメントもなんだかお粗末,というかほっとんど無く,initializeを見てみると

def initialize(source)
  @source = source.respond_to?(:read) ? source.read : source.to_s
  @items = Array.new

  parse
end

パッと見た感じではsourceって何!ってなる.…ファイル?あ,あれか.xmlのファイルもしくはドキュメントそのものだな!
respond_to?ってのは初めて見るんだけど,これはそのオブジェクトに引数で渡された名前(でいいの?)のメソッドがあるか調べるヤツ.

> "hoge".respond_to?(:+)
=> true
> "hoge".respond_to?(:-)
=> false

ファイルならreadってメソッドは確かにあるし,ファイルじゃなかったらとりあえず文字列化しちまえって感じですね.ということで,このsourceにはFileかXMLのなんかオブジェクトを持ってきてやらないとだめと.

とりあえず何か作る

とりあえず何か作って使い方のお勉強.簡単なもの.

# simrss.rb
require 'net/http'
require 'uri'

require 'rubygems'
require 'simple-rss'

url = ARGV.shift

if url
  uri = URI.parse(url)
else
  puts "usage:"
  puts "$ ruby simrss.rb [feed url]"
  exit
end

body = nil
begin
  Net::HTTP.start(uri.host) do |http|
    path = uri.path
    unless uri.query.nil?
      path += '?' + url.query
    end
    
    req = Net::HTTP::Get.new(path)
    res = http.request(req)
    body = res.body.to_s
  end
rescue SocketError
  puts "#{url}?そんなアドレス無いです."
  exit
end

rss = SimpleRSS.new(body)

puts rss.title
puts rss.description
puts rss.link
rss.items.each_with_index do |item, i|
  puts "-" * 30
  puts item.title
  puts item.description
  break if i >= 2
end

とりあえず,url指定してそのフィードからxml落として色々表示って感じでやってみた.実行して見るとこんな感じです.RSSフィードは,phpspot開発日誌さんのを*1

$ ruby simrss.rb 'http://phpspot.org/blog/index.rdf'
phpspot開発日誌
PHPライブラリ紹介と最近のWEB技術情報をお届け
http://phpspot.org/blog/
------------------------------
Yahoo! UI Library を使ったページ送りが可能なグリッドコンポーネント
YUI Grid Sample This is a sample of using the YUI Grid creat...
------------------------------
MeCabを使った形態素解析をAPI経由で簡単に使える『MECAPI』
MECAPI Use "MeCab", the Japanese morphological ana...
------------------------------
PHPで画像に使われている色を簡単に抽出する方法
Color Extract クラス PHPで画像に使われている色を抽出する方法。
Flickr にあるような画像の色検索な...

う〜ん,手軽.

簡単に使い方を纏める

習うより慣れろ,変に勘ぐるよりも,まずコードを書いてみたらフィーリングでできたのですが*2,軽く使い方を纏めておきます.

new

新しくインスタンスを作成するときは,先述した通り,newにxmlのファイルかhttpのリクエストか何かで取ってきたレスポンスのbodyを渡してやればオッケー.

rss = SimpleRSS.new(File.open("file.xml")) # もしくは
# rss = SimpleRSS.new(File.open(response.body)) などなど
フィードの情報を見る

SimpleRSSクラスのアクセサには,

の2つがあります.2つ目のsourceは,読み込んだxmlがStringでそのまんま入ってます.使い道は殆ど無いかな?1つ目のitemsは,行ってみればフィードのエントリーで,配列になっています.なので上のスクリプトではeachでグルグル回して処理できるわけです.

item = simplerss.items[1]
puts item.title

とかでそのエントリーのタイトルを表示してくれるのですが,ご存知の方もおられると思いますが,XML的にはRDFでいうところのdescriptionがAtom feedではsummaryになっていまして*3Atom feedを読み込んでitem.descriptionとしてもnilが返ってくるだけだったりします.この辺りはチョット不便かな.


もっと厄介なのは,このRSS"自体"の要素.titleやdescription(上のスクリプトの,rss.title,rss.description)みたいな.
たとえば,RDFならdescriptionで良いものがAtom feedならtaglineって名前になってて,しかもrss.descriptionってやっても(itemならnilが返ってくるところだけど)NoMethodErrorが発生する.で,試しにtaglineを呼んでみてもNoMmethodError.どうなってんだ?

さらばNoMethodError?

そこで,gemのsimple-rss.rbをちょこっと弄ってみる.

@@feed_tags = [
  :id,
  :title, :subtitle, :link,
  :description, :tagline, # :taglineを追加

こうすると,NoMethodErrorは解消できた.やったね!こんな感じに自分独自でタグを増やしてカスタマイズするってのもアリですね.拡張性があって,良いgemです.

全体的に見て

スゴく便利なのは便利.ただ,読み込んだフィードがRSS 1.0なのかRSS 2.0なのか,はたまたAtom feedなのかその他諸々のRSS 0.9xなのか,わからないのが悔しい.
試しに,feed_tagsにrdfrss,feedを追加してみたんだけど,パースにスゴい時間食ってしまったみたいで,良い解決策とは言えない.なんで3つ増やしただけで恐ろしく遅くなったかはわかんないけれど.


そこさえ何とかなれば,RSSを弄くるツールとしては完璧に近いと思うんですけどね.descriptionでNoMethodErrorが発生したらrescueしてtaglineを呼ぶ,とかするのは楽だろうけど*4,itemの方はnilが返ってきておるし….どーすりゃ良いんでしょうね.

おまけ:インストール時のエラー

インストールしてみると,

Installing RDoc documentation for simple-rss-1.1...

lib/simple-rss.rb:nn:nn: ':' not followed by identified or operator # nには整数

っていうエラーメッセージがめちゃくちゃ出た!だけどirbで試すと

$ irb
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'simple-rss'
=> true

ってなったから問題無いっす.ソースを読んでいると,エラーが起こってる行にはこんなものが.

@@feed_tags = [
  :id,
  :title, :subtitle, :link,
  :description,  # 以下略

RDoc云々のトコでエラーが起こってるけど,RDocで":"は結構危ないのか?知らんけど.まぁGemServerのRDocはちゃんと読めてるし,問題無いってことで…良いよね(大した量のRDocでも無いが^^;).

*1:descriptionが短いし.

*2:ホントRubyって良いよね

*3:実際に同義で定義されているかは知らんけど.ちゃんとした勉強してへんしw

*4:楽だろうと信じたい