はてなアンテナからOPMLを生成する

at2opml.rb


#!/usr/bin/env ruby
require "open-uri"
require "csv"
require "kconv"
require "rexml/document"
$stdout.sync = true

########################################

class Page
def initialize(url)
@url = url
end

def feed
hrss = hatena(@url)
return hrss if hrss
open(@url) {|f|
f.read.scan(/<link\b[^>]+>/i) {|m|
if attr(m, "rel") == "alternate" || attr(m, "type") == "application/rss+xml"
return attr(m, "href")
end
}
}
return nil
end

private
def hatena(url)
return (url =~ %r|^(http://d.hatena.ne.jp/[^/]+)/?$|) ? $1 + "/rss" : nil
end

def attr(elem, name)
elem.slice(Regexp.new('\b' + name + '="?([^"\s]+)"?\b'), 1)
end
end

########################################

class OPML
def initialize(title)
@doc = REXML::Document.new
@doc << REXML::XMLDecl.new("1.0", "UTF-8")
root = @doc.add_element("opml", "version"=>"1.0")
root.add_element("head").add_element("title").text = title
@body = root.add_element("body")
end

def add(title, url)
page = Page.new(url)
feed = page.feed
if feed
puts title + " => " + feed
attrs = {"title"=>title, "htmlUrl"=>url, "xmlUrl"=>feed, "type"=>"rss"}
@body.add_element("outline", attrs)
else
puts "warning: #{title}のフィードが見つかりませんでした"
end
end

def write(output, indent=-1, transitive=false)
@doc.write(output, indent, transitive)
end

def to_s
return @doc.to_s
end
end

########################################

def get_lirs(id)
lirs = nil
begin
puts "#{id}のアンテナを開きます..."
f = open("http://a.hatena.ne.jp/#{id}/source?mode=lirs")
lirs = CSV.parse(f.read.tosjis)
ensure
f.close if f
end
return lirs
end

########################################

unless ARGV.length == 1
puts "at2opml.rb はてなID"
exit
end

id = ARGV[0]
filename = "antenna.xml"
lirs = get_lirs(id)
opml = OPML.new("はてなアンテナ - #{id}のアンテナ")
lirs.each {|row| opml.add(row[6], row[5])}
puts "#{filename}を書き出します..."
File.open(filename, "w") {|f|
opml.write(f, 0)
}

実行結果


~$ ./at2opml.rb winebarrel
winebarrelのアンテナを開きます...
a-sanの日記 => http://d.hatena.ne.jp/a-san/rss
chibafの日記 => http://d.hatena.ne.jp/chibaf/rss
...
antenna.xmlを書き出します...
~$