HTTP::Responseに格納された日本語ページをちゃんとデコードしてunicodeで持ちたかったので最初
my $body = $response->decoded_content();

ってやってたんだけど、これだとcp932 (!= Shift-JIS)でこけることが多くて、とりあえずヘッダーとMETAヘッダで見るかと思って以下のようにして、decoded_content() にcharsetが必要であれば渡されるようにした。
my %opts; if ( my $ct = $res->content_type ) { if ($ct =~ /charset=Shift_JIS/) { $opts{charset} = 'cp932'; } } if ( my $ref = $res->content_ref ) { if ($$ref =~ /charset=Shift_JIS/) { $opts{charset} = 'cp932'; } } my $body = $res->decoded_content(%opts);

しかしこれでも化ける。一番化けるのはcontent-typeにもMETA部分にもchrasetが指定されてない場合。ここでようやくHTTP::Responseのコードを読んだら、charset_strictってオプションつけるとFB_CROAK() をEncode::decode() に渡してくれる事に気づいた。これならせめてエラーは分かるだろ、と思って以下のようにした。
my %opts = ( charset_strict => 1, ); if ( my $ct = $res->content_type ) { if ($ct =~ /charset=Shift_JIS/) { $opts{charset} = 'cp932'; } } if ( my $ref = $res->content_ref ) { if ($$ref =~ /charset=Shift_JIS/) { $opts{charset} = 'cp932'; } } my $body = $res->decoded_content(%opts);

それでもまだ駄目。っていうかFB_CROAK()してるのにcroakしないでやんの。と、思ってもう一回ソースコードを読んだら、
my $charset = $opt{charset} || $ct_param{charset} || $opt{default_charset} || "ISO-8859-1";

とかしてcharsetに何も指定しないとiso-8859-1とかになるようになってる!これじゃあなんでも適当にデコードしちゃうじゃないか。というわけで、このような状態の時に最も使われている確率の高いcp932をデフォルトの文字コードとして使用するようにしてみた。これでとりあえず動いた:
my %opts = ( charset_strict => 1, default_charset => 'cp932', ); if ( my $ct = $res->content_type ) { if ($ct =~ /charset=Shift_JIS/) { $opts{charset} = 'cp932'; } } if ( my $ref = $res->content_ref ) { if ($$ref =~ /charset=Shift_JIS/) { $opts{charset} = 'cp932'; } } my $body = $res->decoded_content(%opts);
追記:なんか書き忘れてるなぁ、と思っていたらやっぱり忘れてた。ここまでだと、まだcp932じゃないページで、なおかつヘッダー等から情報が得られない場合はまだ変なことになる。なので、さっきのcharset_strictとあわせて、さらにハック
my %opts = ( charset_strict => 1, default_charset => 'cp932', ); if ( my $ct = $res->content_type ) { if ($ct =~ /charset=Shift_JIS/) { $opts{charset} = 'cp932'; } } if ( my $ref = $res->content_ref ) { if ($$ref =~ /charset=Shift_JIS/) { $opts{charset} = 'cp932'; } } my $body; eval { $body = $res->decoded_content(%opts); }; if ($@) { foreach my $charset qw(cp932 euc-jp iso-2022-jp utf8) { eval { $body = $res->decoded_content(%opts, charset => $charset); }; last unless $@; } }
まぁハックだけどとりあえず動く。