Rubyでどう書く?:重複したRSSをまとめる

高瀬裕一(KBMJ)
2008-06-25 08:00:00
  • このエントリーをはてなブックマークに追加

 第4回目のテーマは「重複したRSSをまとめる」と題しまして、Rubyで複数のRSSから重複したエントリを除去する問題を出します。

問題

 最近、はてなブックマークやlivedoor クリップなど、ユーザーが外部のサイトを集めて、その結果を出力するサービスが増えてきました。

 ただ、一部のサイトを見ているだけで話題を追うことができるので便利なのですが、同じURLが複数のRSSにあり「もうこれは見たのに……」という事も多いですよね。

 そこで今回は、複数のRSSを一つのRSSとして出力するプログラムを問題にします。

仕様

  • RSSのURLは、コマンドラインから引数として1個以上与えられる。
  • 結合した結果のRSSのタイトルおよび説明は、各引数のタイトルをつなげたものとする。
  • 結合した結果のRSSのURLは第一引数のURLとする。
  • フィードのリンク先が同一のものを同じフィードとして扱う。
  • 出力順は第一引数のものを順に出力し、その後第二引数、第三引数のものを順に出力する(同じフィードとして扱うものは除外する)。
  • 重複したものは前の引数の内容を出力する。
  • 出力先は標準出力とする。
  • 出力するRSSのバージョンは2.0

回答例

  1 require 'rss'
  2 
  3 rss_feeds = []
  4 rss_urls = []
  5 title="" 
  6 
  7 ARGV.each do | rss_url |
  8   open(rss_url) do | http |
  9     response = http.read
 10     rss_results = RSS::Parser.parse(response, false)
 11     
 12     if rss_results && rss_results.channel && rss_results.channel.title
 13       title += " と " if title.size > 0
 14       title += rss_results.channel.title
 15     end
 16     
 17     rss_results.items.each do | item |
 18       unless rss_urls.include? item.link
 19         rss_urls << item.link
 20         rss_feeds << item
 21       end
 22     end
 23   end
 24 end
 25 
 26 rss = RSS::Maker.make("2.0") do | writer |
 27   writer.channel.title = title || ""
 28   writer.channel.link = rss_urls[0] || ""
 29   writer.channel.description = title || ""
 30   rss_feeds.each do | feed | 
 31     feed.setup_maker(writer)
 32   end
 33 end
 34 
 35 puts rss.to_s

 試しに、livedoor クリップ 人気ページと、はてなブックマーク 人気エントリーを取得してみましょう。

ruby rss.rb http://clip.livedoor.com/rss/hot http://b.hatena.ne.jp/hotentry?mode=rss

 本記事を執筆時点では、実行結果は以下のようになります。



  
    livedoor クリップ - 人気ページ と はてなブックマーク 最近の人気エントリー
    http://clip.livedoor.com/rss/hot
    livedoor クリップ - 人気ページ と はてなブックマーク 最近の人気エントリー
08-06-23 22:52 $ ruby "test.rb" "http://clip.livedoor.com/rss/hot" "http://b.hatena.ne.jp/hotentry?mode=rss" | head -20


  
    livedoor クリップ - 人気ページ と はてなブックマーク 最近の人気エントリー
    http://clip.livedoor.com/rss/hot
    livedoor クリップ - 人気ページ と はてなブックマーク 最近の人気エントリー
    
      Engineer25 すべてを楽しむ若きスーパーエンジニア達 第4回 cho45氏- ウェブキャリア
      http://www.web-career.com/contents/engineer25/4.html
      
      interview
      ruby
      javascript
      program
    
    
   ...中略...
    
  

解説

 本サンプルプログラムは、大きく3段階の処理になっています。

 第1段階(6行目から9行目)では、渡された引数を元に各ページにアクセスし、RSSを解析しています。

 第2段階(11行目から20行目)では、rss_feeds内にRSSを分解したフィードの内容を登録しています。

 今回は第一引数の出力内容を優先するので、順番を管理しやすいArrayに登録しました。そして、重複チェック用に、rss_urlsというArrayを作って、登録時にそこにurlを入れ、urlが既に登録されていないかチェックをしています。

 第3段階目(25行目から34行目)では、全RSSの取得を完了した後で、makeでRSSを再生成して出力しています。

最後に――

 今回のお題は主に自分が必要と感じるものを作ってみました。

 パーサが標準のライブラリにあるので特に手間無く作れましたが、もうちょっと実用度を上げようとすると、サーバが落ちている場合や、引数が間違えている場合などの例外処理を組み込むのも良いと思います。

 こんな風に作ってみたよという方が居られましたら、コメントやbuilderブログなどで教えていただきたいと思います。

  • コメント(2件)
#1 someeda   2008-06-27 00:15:08
rss_feeds = rss_urls = []

これだと rss_feeds と rss_urls は同じオブジェクトを指しますが、意図したとおりに動いているのでしょうか?
#2 tomita   2008-06-30 12:50:21
someeda様、いつもbuilderをご愛読頂きありがとうございます。
builder編集部の冨田です。

ご指摘の通りです。「回答例」のコードを下記のように修正しました。

このほかにもっと良い回答例があれば、是非コメントで教えて下さい。
ご指摘ありがとうございました。
1 require 'rss'
  2 
  3 rss_feeds = rss_urls = []
  4 title="" 
  5 

↓

  1 require 'rss'
  2 
  3 rss_feeds = []
  4 rss_urls = []
  5 title="" 
  6
  • 新着記事
  • 特集
  • ブログ