踩到 WWW::Mechanize 的地雷

踩到 WWW::Mechanize 的地雷
http://blog.leeym.com/2007/03/wwwmechanize.html
WWW::Mechanize 是我最常用的 module 之一,用它來寫機器人,除了 JavaScript 不行以外,大部分網站都能快速上手。前一陣子 (www/p5-WWW-Mechanize/Makefile、PR ports/109847),升級到 1.22 以後,程式陸陸續續出現問題,一些網站用 LWP::Simple 抓就沒問題,改用 WWW::Mechanize 抓就變亂碼。這問題一開始也不嚴重,不是每個網站都會發生,所以我也沒注意,直到今天寫一支新的工具,每次執行都是亂碼,才下定決心除錯。

一路往上找 WWW::Mechanize 的上一層是 LWP::UserAgent,而 WWW::Mechanize 的 get 會呼叫上一層的 get,所以我先在 LWP::UserAgent 埋了一堆暗樁,看看是不是 request 或者 response 有什麼不同。反覆執行了好幾次,發現 LWP::UserAgent 抓到的都是正常的,那問題應該出在 WWW::Mechanize。接下來我繼續追 WWW::Mechanize,發現 get 回來的資料還是正常的,一步一步查,最後終於發現問題出在 _update_page。有問題的在這一段:

sub _update_page {
my ($self, $request, $res) = @_;
….
my $content = $res->decoded_content;
$content = $res->content if (not defined $content);

if ($self->is_html) {
$self->update_html($content);
}
else {
$self->{content} = $content;
}

return $res;
} # _update_page

$self->{content} 的內容在 _update_page 被更動過,檢查一下,果然就是它。原來這一段程式碼在 1.21_01 被加入 WWW::Mechanize,但因為 1.21_0[1-4] 都是測試版,所以直到 1.22 才被公布出來。很不巧的是也許作者認為改這個沒什麼,在 Change 中對於這項改變隻字未提,所以我就這樣踩進了這個地雷。

在 WWW:Mechanize 1.20 以前,WWW::Mechanize 抓到什麼就吐什麼;在 WWW::Mechanize 1.21_01 以後,WWW::Mechanize 優先採用 decoded_content,失敗才採用 content。而 decoded_content 和 content 的差別,是 decoded_content 經過一次 Encode::decode($charset, $content)。因此,若新版 WWW::Mechanize 讀取的頁面是 ISO-8859-1、ASCII 或者 UTF-8 的,應該不會有什麼問題;如果不是,那 decoded_content 就變成一陀亂碼。

解法呢?

請像下面這樣抓到頁面之後,指定轉換為 $charset 編碼,就可以避免輸出亂碼了。這樣好像有點麻煩,也許在之後的版本會多加上一些參數來改變挑選 decoded_content 或者 content 的行為吧。

$mech->get($url);
$mech->update_html(Encode::encode($charset, $mech->content));
張貼者: leeym 於 下午9:49

本篇發表於 perl。將永久鏈結加入書籤。

發表留言