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 $@;
}
}
まぁハックだけどとりあえず動く。
コメント