RSS maker on Ruby(ゴルゴ31篇)

(2009-04-27追記 Google App EngineでRSS - 鯨飲馬食コードにリンクあり)

RSS on Ruby - 鯨飲馬食コード」で書いた、「ゴルゴ31」のRSSを出力するRubyスクリプトをメモしておく。ごちゃごちゃしてるしエラー処理も考えずにアドホックに作ったものなので役に立つかは分からないけど。

以下コード。まずMy_RSSクラスから。

require 'uri'
require 'net/http'
Net::HTTP.version_1_2
require 'rss'
require 'cgi'
require 'stringio'

class My_RSS
  @target_url = ""
  @target_name = ""
  @target_description = ""
  @rss_url = ""
  attr_accessor :target_url, :target_name, :target_description, :rss_url
  @start_line = //
  @end_line = //
  @cond_of_para =//
  @cond_of_anchor = //
  attr_accessor :start_line, :end_line, :cond_of_para, :cond_of_anchor
  @num_link = 0
  @num_title = 0
  @num_description = 0
  @num_rest = 0
  attr_accessor :num_link, :num_title, :num_description, :num_rest
  @a_link = []
  @a_title = []
  @a_description = []

  def scan_body(body)
    buffer = ""
    a_link = []
    a_title = []
    a_description = []
    flag = 0

    StringIO.open(body).each do |line|
      line = CGI::unescapeHTML(line)
      if flag == 0 && line =~ @start_line
        flag = 1
      elsif flag == 1 && line =~ @end_line
        break
      end

      if flag > 0
        buffer = buffer + line.chomp
        if buffer =~ @cond_of_anchor
          link = $~[@num_link]
          title = $~[@num_title]
          description = $~[@num_description]
          rest = $~[@num_rest]
          if link =~ /^http\:\/\//
            a_link.push(link)
            a_title.push(CGI::escapeHTML(title))
            a_description.push(description)
          end
          buffer = rest
        end
      end
    end
    @a_link = a_link
    @a_title = a_title
    @a_description = a_description
  end

  def make_rss
    i = 0
    rss = RSS::Maker.make("1.0") do |maker|
      maker.channel.about = @rss_url
      maker.channel.title = @target_name
      maker.channel.description = @target_description
      maker.channel.link = @target_url

      while i < @a_link.size
        maker.items.new_item do |item|
          item.link = @a_link[i].to_s
          item.title = @a_title[i].to_s
          item.description = @a_description[i].to_s
        end
      i+=1
      end
    end
    return rss
  end

end

単に正規表現でマッチした部分を抜き出しているだけ。CGI::escapeHTMLで文字列をエスケープしてるけど必要ないかもしれない。上のクラスをMy_RSS.rbに保存したとして、以下のコードで実行する。

require 'My_RSS'

require 'time'
require 'kconv'
$KCODE = "utf8"

rss = My_RSS.new
rss.target_url = 'http://www.golgo31.net/'
rss.target_name = Kconv::toutf8('ゴルゴ31')
rss.target_description = Kconv::toutf8('ゴルゴ31(http://www.golgo31.net/)の俺専用RSS')
filename = 'golgo31.xml'
rss.rss_url = 'http://yourhost.com/' + filename
rss.start_line = /\<a name=\"(\d*?)\"/i
rss.end_line = /\<\/font\>\<\/td\>\<\/tr\>/i
rss.cond_of_anchor = /\<a href=\"(.*?)\"(.*?)\>(.*?)\<\/a\>(?:\<BR\>)?(.*?)\<BR\>\<BR\>(.*)/i
rss.num_link = 1
rss.num_title = 3
rss.num_description = 4
rss.num_rest = 5

# sinseに10時間前をセット
since = ( 0*(24*60*60) + 10*(60*60) + 0*(60) + 0 )
time = Time.now - since

uri = URI.parse(rss.target_url)
http = Net::HTTP.new(uri.host)
# since秒以内に更新があったかを問い合わせ
response = http.get(uri.path, {'if-modified-since' => time.httpdate})

# レスポンスコードが200ならファイルに書き出し
if response.code.to_i == 200
  rss.scan_body(Kconv::toutf8(response.body))

  output = open(filename, "w")
  made_rss = rss.make_rss
  output.print made_rss
  output.close
end

こんな感じで、今のところ安定して動作しているみたい(「RSSログ Index of /rss」)。cronを設定して定期的に実行するといいよ。

一応書いておくけど、私は普段個人ニュースサイトなるものを見ない。今回RSSRDF Site Summary)を作ったのも、単にSemantic Webの思想に興味を持っているから。サイトの情報の要約が欲しかっただけで、別に更新情報をいち早く手に入れるためではないよ。

使えそうだなと思ったら改変していろんなサイトのリンクベースのRSSを作るといいよ。そして公開してくれると嬉しいな。