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で以下のようなやりとりを発見。
tdtds | RSS::Paeserが「twitterなんてnamespaceねぇよ」つってるんだけど、ちゃんとxmlnsあるんだよなぁ。 | 2010/05/04(火) 08:28:59 | |
tdtds | xmlns:twitterは、RSS 2.0では未サポートという意味か。じゃあTwitter側の問題? でもなんで今まで動いてたんだ? | 2010/05/04(火) 08:35:00 | |
ktou | @tdtds どんな感じになるんですか?知らない要素は無視するようにできた気がします。 | 2010/05/04(火) 08:38:06 | |
tdtds | @ktou おお、一番詳しい人登場! 「 `check_ns': prefix <twitter> doesn't associate uri <> in tag <source> (RSS::NSError)」と言われる。xmlnsがtwitterの要素を無視させればいいの? | 2010/05/04(火) 08:40:21 | |
tdtds | @ktou ちなみにTwitterのuser_timelineのfeedにある「<twitter:source>」が元凶です。 | 2010/05/04(火) 08:41:25 | |
tdtds | @ktou Parser::parseの第2、第3引数をfalseにしたら通った! ありがとう! | 2010/05/04(火) 08:45:50 | |
ktou | @tdtds RSS::Parser.parseの引数ってどうなっていますか?falseにすると無視しなくなるんです。(デフォルトはtrue。このAPI、わかりづらくてよくない!) | 2010/05/04(火) 08:47:08 | |
ktou | @tdtds 何もしていないですが!たぶん、第2引数をfalseにするだけでいけます! | 2010/05/04(火) 08:48:41 | |
tdtds | @ktou 十分なヒントをくれたということよw ちょっと時間ないんで、他のパターンはあとで試してみます | 2010/05/04(火) 08:50:01 |
念のため、引数の意味を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じゃないので変な例外が発生してもしょうがない?