「VOA Special EnglishのPodcast用RSS生成 for Ruby」を勝手に添削 (できるほどRubyistではないのに)

うっかりTOEICを申し込んでしまったので、ちょっと本気で英語勉強しよ!と思っているbonlifeです。英語に耳を慣らすためにPodcastの購読を増やすことを検討。VOAあたりが良いってウワサを聞いたことがあったので調べてみると、何やら便利そうなものを公開してる人発見です!

ほうほう、と思ってリンク先のリンク先のRSSiTunesに登録すると、descriptionが出てくるヤツと出てこないヤツがあってガッカリ…。仕方なくあんまり得意じゃないRubyのコードを見てみると、以下のような箇所が…。

	body = if xhtml =~ /<span class="body">(.+?)<\/span>/mi then $1 else "" end

うわぁん、なぜか最短一致使ってる!これだと関係ないspanタグが途中にあると、そこで終わってしまう罠。
思い立ったが吉日生活、ということで頑張ってRubyでコード書き直してみました。Rubyのコードとして根本的に色々間違ってそうな予感です。 (「Hpricot で link タグが勝手に空要素になる - kaeruspoon」の現象でハマったのは秘密です。)

  • VOARSSGenerator.rb
#!/usr/bin/ruby
# VOA Special English Poscast Generator

require 'cgi'
require 'date'
require 'hpricot'
require 'open-uri'

class VOARSSGenerator

  @@TARGET_URL = "http://www.voanews.com/specialenglish/customCF/RecentStoriesRSS.cfm?keyword=TopStories"

  def generate_voa_rss
    generate_rss_header
    xml = Hpricot.XML(open(@@TARGET_URL))
    (xml/"rss/channel/item").each do |item|
      title = (item/"title").text
      link  = (item/"link").text
      mp3, wordcount, date, description = parse_each_link(link)
      generate_item_xml(title, link, mp3, wordcount, date, description)
    end
    generate_rss_footer
  end

  def generate_rss_header
    puts <<HEAD
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>VOA Special English</title>
<link>http://www.voanews.com/specialenglish/</link>
<description>VOA Special English</description>
<language>en</language>
<pubDate>#{DateTime.now.strftime('%a, %d %b %Y %H:%M:%S %z')}</pubDate>
HEAD
  end

  def generate_rss_footer
    puts <<FOOT
</channel>
</rss>
FOOT
  end

  def parse_each_link(link)
    doc = Hpricot(open(link))
    ((doc/"span.body/")./".imagecaption").remove
    description = (doc/"span.body").first.inner_html.gsub(/\n+/," ").gsub(/<br(?: ?\/)?>/,"\n").gsub(/<\/p>/,"\n\n").gsub(/&nbsp;/," ").gsub(/<\/?[^>]*>/, " ").gsub(/ +/," ").gsub(/^ /,"").sub(/^\s+/,"").sub(/\s+\Z/m,"")
    date = Date.parse((doc/"span.datetime/em").first.inner_html)
    mp3 = ((doc/"a.media-asset").first)[:href]
    wordcount = description.scan(/\w+/).length
    return mp3, wordcount, date, description
  end

  def h(st)
    CGI::escapeHTML(st)
  end

  def generate_item_xml(title, link, mp3, wordcount, date, description)
    puts <<ITEM
<item>
<title>[#{wordcount}] #{h title}</title>
<link>#{h link}</link>
<pubDate>#{date.strftime('%a, %d %b %Y %H:%M:%S %z')}</pubDate>
<dc:creator>VOA</dc:creator>
<dc:subject>English</dc:subject><dc:subject>News</dc:subject>
<description><![CDATA[
#{description}
]]></description>
<enclosure url="#{h mp3}" length="" type="audio/mpeg" />
</item>
ITEM
  end

end

v = VOARSSGenerator.new
v.generate_voa_rss

parse_each_link の description まわりの処理がおそろしく力技。しかも微妙。関数に値を色々と渡してるところもダサいけど、まぁ良いか。後、CDATAに入れるデータがどうなってれば良いのか、とかXMLの基本が分かってないんですよね。(そのあたりはまた後で勉強しよっと。) id:n_shuyoさんが部分的に修正してくれると嬉しいなぁ、とかなんとか。