http://www.largevocalmix.jp/diary/2008/02/11
ブログの記事別に更新時刻を確認できるようにした。しばらく動かして問題なければ念願の「しょこたん☆ぶろぐ」の取り込みですよ、奥さん。
#!/home/_/opt/ruby/bin/ruby -Ku #------------------------------------------------------------ # 巡回用アンテナ # ・仕様 # ・失敗したら更新時刻を平成元年としてソート順の最後にする # ・RSSは実行時の日付を含む過去の記事を取得する # ・メモ # ・コーディング規約 # http://www.loveruby.net/w/RubyCodingStyle.html # ・Time.getlocal.w3cdtf # require 'rss' -> alias iso8601 -> alias xmlschema -> 文字列 # ・繰り返しの制御 # break : 繰り返しを中断し、繰り返しの中から抜ける # ・文字列 # ・「""」を使えば「\」を使った特殊文字を表現することができる # ・%記法「%()」を使えばダブルクォート文字列を表現することができる # http://www.ruby-lang.org/ja/man/html/_A5EAA5C6A5E9A5EB.html#a.25.b5.ad.cb.a1 #------------------------------------------------------------ require 'rexml/document' require 'net/http' require 'time' require 'rss' require 'cgi' #------------------------------------------------------------ # 定数の定義 # ・定数はアルファベットの大文字で始まる #------------------------------------------------------------ APPLICATION_PATH = '/home/_/app/' PUBLIC_PATH = '/home/_/www/meegle/' SITE_LIST_XML_PATH = APPLICATION_PATH + '_' ANTENNA_TEMPLATE_PATH = APPLICATION_PATH + '_' ANTENNA_HTML_PATH = PUBLIC_PATH + '_' CHECK_LAST_MODIFIED = 'LastModified'.downcase CHECK_FEED = 'Feed'.downcase RSS10 = 'RSS::RDF'.downcase RSS20 = 'RSS::Rss'.downcase HEISEI_GANNEN = '1989-01-08T00:00:00+09:00' COMPARE_DATE_FORMAT = '%Y%m%d' CURRENT_DATE = Time.now.getlocal.strftime(COMPARE_DATE_FORMAT) MAX_ARTICLE_COUNT = 7 USER_AGENT = '_' #------------------------------------------------------------ # クラスの定義 # ・クラス名は大文字で始める # ・インスタンス変数は '@' で始まる # ・読み書き可能なインスタンス変数は attr_accessor で定義できる # ・Class.new で initialize が呼び出される # ・オブジェクトにとって必要な初期化処理は initialize に記述する # ・super でスーパークラスの同名メソッドを呼ぶ #------------------------------------------------------------ class Utils def self.kako?(date) date.strftime(COMPARE_DATE_FORMAT) <= CURRENT_DATE end def self.trim_date(date) date.sub(/T/, ' ').sub(/\+09:00/, '') end end class Article attr_accessor :update_date, :title, :link def initialize @update_date = HEISEI_GANNEN @title = '' @link = '' end def format_date Utils.trim_date(@update_date) end end class Antenna attr_accessor :title, :uri, :feed, :update_date, :preview def initialize(in_title, in_uri, in_feed='') @title = in_title @uri = URI(in_uri) unless in_feed.empty? @feed = URI(in_feed) else @feed = nil end @update_date = HEISEI_GANNEN @preview = [] end def format_date Utils.trim_date(@update_date) end def <=> (other) @update_date <=> other.update_date end end class LastModified < Antenna def preview_error(cause) @preview.push(cause) end def to_html %(<li>[#{format_date}] <a href="#{@uri.to_s}">#{@title}</a><div class="preview">#{@preview[0]}</div></li>) end def check req = Net::HTTP::Head.new(@uri.path) req['User-Agent'] = USER_AGENT res = Net::HTTP.start(@uri.host, @uri.port) do |http| http.request(req) end case res when Net::HTTPSuccess @update_date = Time.parse(res['Last-Modified']).getlocal.w3cdtf @preview.push(res.class.name) else #HTTPステータス異常 preview_error(res.class.name) end end end class Feed < Antenna attr_accessor :error def initialize(in_title, in_uri, in_feed) super(in_title, in_uri, in_feed) @error = false end def preview_error(cause) article = Article.new article.title = cause @preview.push(article) @error = true end def preview_html html = '<ul class="preview">' unless error @preview.each_with_index do |article, i| if i % 2 == 0 html += %(<li class="alternate">[#{article.format_date}] <a href="#{article.link}">#{article.title}</a></li>) else html += %(<li>[#{article.format_date}] <a href="#{article.link}">#{article.title}</a></li>) end end else html += %(<li>[#{@preview[0].format_date}] #{@preview[0].title}</li>) end html += '</ul>' end def to_html %(<li>[#{format_date}] <a href="#{@uri.to_s}">#{@title}</a><div class="preview">#{preview_html}</div></li>) end # jugem.jp/?mode=rss用 def path if @feed.query == nil @feed.path else "#{@feed.path}?#{@feed.query}" end end def check req = Net::HTTP::Get.new(path) req['User-Agent'] = USER_AGENT res = Net::HTTP.start(@feed.host, @feed.port) do |http| http.request(req) end case res when Net::HTTPSuccess rss = RSS::Parser.parse(res.body) case rss.class.name.downcase when RSS10 rss.items.sort_by{|a| a.dc_date}.reverse.each do |item| if Utils.kako?(item.dc_date.getlocal) @update_date = item.dc_date.getlocal.w3cdtf if @preview.empty? article = Article.new article.update_date = item.dc_date.getlocal.w3cdtf article.title = item.title.chomp article.link = item.link @preview.push(article) break if @preview.size == MAX_ARTICLE_COUNT end end when RSS20 #RSS 2.0でもDublin Coreモジュールを使う場合がある if rss.channel.items.first.dc_date rss.channel.items.sort_by{|a| a.dc_date}.reverse.each do |item| if Utils.kako?(item.dc_date.getlocal) @update_date = item.dc_date.getlocal.w3cdtf if @preview.empty? article = Article.new article.update_date = item.dc_date.getlocal.w3cdtf article.title = item.title.chomp article.link = item.link @preview.push(article) break if @preview.size == MAX_ARTICLE_COUNT end end else rss.channel.items.sort_by{|a| a.pubDate}.reverse.each do |item| if Utils.kako?(item.pubDate.getlocal) @update_date = item.pubDate.getlocal.w3cdtf if @preview.empty? article = Article.new article.update_date = item.pubDate.getlocal.w3cdtf article.title = item.title.chomp article.link = item.link @preview.push(article) break if @preview.size == MAX_ARTICLE_COUNT end end end end else #HTTPステータス異常 preview_error(res.class.name) end end end #------------------------------------------------------------ # メソッドの定義 # ・戻り値は最後に評価した式になる # ・return で直接戻り値を返せる #------------------------------------------------------------ def read_site_list site_list = Array.new xml = REXML::Document.new(File.new(SITE_LIST_XML_PATH)) xml.elements.each('sitelist/site') do |site| info = site.elements case info['type'].text.downcase when CHECK_LAST_MODIFIED site_list.push(LastModified.new(info['title'].text, info['uri'].text)) when CHECK_FEED site_list.push(Feed.new(info['title'].text, info['uri'].text, info['feed'].text)) end end return site_list end def write_antenna(site_list) template = File.read(ANTENNA_TEMPLATE_PATH) antenna = '' indent = ' ' * 8 site_list.each do |site| antenna += indent + site.to_html + "\n" end template.sub!(/antenna_body/, antenna) template.sub!(/ruby_version/, RUBY_VERSION) template.sub!(/rss_parser_version/, RSS::VERSION) File.open(ANTENNA_HTML_PATH, 'w') do |writer| writer.puts(template) end File.chmod(0604, ANTENNA_HTML_PATH) end #------------------------------------------------------------ # 処理の開始 #------------------------------------------------------------ site_list = read_site_list site_list.each do |site| begin site.check rescue => ex site.preview_error(ex.class.name) rescue Timeout::Error site.preview_error('Timeout::Error') end end write_antenna(site_list.sort.reverse)