にたまごほうれん草アーカイブ

はてなダイアリーで書いてた「にたまごほうれん草」という日記のアーカイブです。現在は「にたまごほうれん草ブログ」を運営中です。

TwitterのユーザタイムラインのRSSをパースするときにエラーが出た場合の対処法

現象

5月4〜5日のどこかで、Twitterの吐くユーザタイムラインが変わったようだ。
その前を正確に把握していないのだが、現在のRSS(たとえば私のツイートのRSSこんな感じ)では、

<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:georss="http://www.georss.org/georss" xmlns:twitter="http://api.twitter.com">
...
<twitter:source>
  <a href="http://www.atebits.com/" rel="nofollow">Tweetie</a>
</twitter:source>
<twitter:place/>
...
</rss>

という風に、twitter名前空間ができている。
私は、このRSSを元にRubyスクリプトで自分のツイートを抽出し、ブログにまとめているのだが、この名前空間のせいで(?)スクリプトが以下のような例外を吐くようになっていた。

RSS::NSError - prefix <twitter> doesn't associate uri <> in tag <source>:
 /usr/lib/ruby/1.8/rss/parser.rb:419:in `check_ns'
 /usr/lib/ruby/1.8/rss/parser.rb:449:in `start_have_something_element'
 /usr/lib/ruby/1.8/rss/parser.rb:396:in `start_else_element'
 /usr/lib/ruby/1.8/rss/parser.rb:347:in `tag_start'
 /usr/lib/ruby/1.8/rexml/parsers/streamparser.rb:24:in `parse'
 /usr/lib/ruby/1.8/rexml/document.rb:202:in `parse_stream'
 /usr/lib/ruby/1.8/rss/rexmlparser.rb:22:in `_parse'
 /usr/lib/ruby/1.8/rss/parser.rb:164:in `parse'
 /usr/lib/ruby/1.8/rss/parser.rb:79:in `parse'
 postmytweets.rb:95:in `get_user_timeline'
(後略)

対処法

この場合、先に結論を言うと、RSS::Parser.parseを使っているならば、第二引数にfalseを指定(バリデーション無しでパース)することで例外が発生しなくなる。

# 例
# idとpには適当な数字が入る
rssdoc = open("http://twitter.com/statuses/user_timeline/#{id}.rss?page=#{p}").read
rss = RSS::Parser.parse(rssdoc, false) # <- ココ!

あんまり納得の行く対処法じゃないけど、RSSが修正されるまではこうするしかなさそうだ。

解決までの経緯など

困っていたらググってみる、ということで、検索したら、Twitterで以下のようなやりとりを発見。

というわけで、RSS::Parser.parseの第二引数でfalseを指定すればいけるようになったのでひとまず解決。
念のため、引数の意味をRSS Parser::Tutorial.jaで調べると、第二引数は「バリデーションを行う(true)/行わない(false)」を指定するものとのこと。
バリデーションしなければ通るということは、元のRSSはvalidではないということになるのか?
というわけで、W3C Feed Validation Service, for Atom and RSSに該当RSSを突っ込んで調べてみる(結果)と、

ine 17, column 0: Missing namespace for a (20 occurrences) [help]

<a href="http://www.atebits.com/" rel="nofollow">Tweetie</a>    </twitter:so ...

あ、aタグに名前空間指定がない。。。というわけで一目瞭然でした。

ちなみに、

第二引数をtrueに、第三引数をfalseにすると、以下のような例外が。

RSS::NotExpectedTagError - tag <{http://www.w3.org/2005/Atom}link> is not expected in tag <channel>:
 /usr/lib/ruby/1.8/rss/parser.rb:405:in `start_else_element'
 /usr/lib/ruby/1.8/rss/parser.rb:254:in `start_link'
(後略)

この例外が起こる理由がよくわからないのだけどなんでだろう。そもそもvalidじゃないので変な例外が発生してもしょうがない?