del.icio.usのRSSをXML::RSSでparseする前に

del.icio.usで日本の記事をブックマークする際に、extended欄に記事から長い文章を貼り付けると、たまに文字化けすることがあります。
feedburnerでdel.icio.usが文字化けする問題 -huixingの日記-

普通に使うだけなら、あーなんか最後が?になってるけどまぁいいか、で済ませられるんですが、このRSSperlXML::RSSでparse使用とすると、

not well-formed (invalid token) at line 31, column 92, byte 2258 
at /usr/local/lib/perl5/site_perl/5.8.6/ppc-linux/XML/Parser.pm line 187

と言われてしまいます(上記は実験用RSSをparseした場合の出力)。

このエラーメッセージを元に検索すると次のページが見つかりました。

not well-formed (invalid token) at line 1, column 34922, byte 50440 at /usr/lib/perl5/5.8.0/i386-linux-thread-multi/XML/Parser.pm line 185
調べてみると,SOAPレスポンスメッセージの中の「レビュー」部分に機種依存の文字が含まれているため,うまくparseできないようだ.
XML::Parserのエラー -test::blog-

上記はAmazonSOAPレスポンスの問題ですが、del.icio.usRSSもencoding=utf-8なので、同じ問題のようです。

いろいろ試してみて、getしたRSSutf-8 -> euc-jp -> utf-8とすることで解決しました。eucに変換することで、utf-8の場合に人間には?で見えていたu+fffdの文字を本来の?に変換し、文字としての?をまたutf-8に戻しています(機種依存文字の解決だけなら最後のutf-8に戻す処理は要らないです)。

#!/usr/local/bin/perl -w
use strict;
use warnings;
use LWP::Simple;
use XML::RSS;
use Encode;
use encoding "euc-jp";

my $url = 'http://del.icio.us/rss/rtk2106/_tmp+perl';

my $content = get($url);
my $rss = XML::RSS->new;

$content = _convert_encode($content);
$rss->parse($content);

my $output;
foreach my $item (@{$rss->{items}}) {
	$output .= "--------------------\n";
	$output .= "title : $item->{title}\n";
	$output .= "link : $item->{link}\n";
	$output .= "description : $item->{description}\n";
}
print $output;

sub _convert_encode {
	my ($content) = shift;
  
	my ($enc_src) = ($content =~ /<\?xml .*? encoding="(.*?)"\s*\?>/);
	my $enc_dst = 'euc-jp';
	Encode::from_to($content, $enc_src, $enc_dst);
	Encode::from_to($content, $enc_dst, $enc_src);
  
	return $content;
}