使用 simple_html_dom 抓取 GB2312 頁面並轉為 UTF-8 時,中文變亂碼:
4月26日在<span>鏈濋槼</span>公園... 第7個出場的<span>鍖椾含</span>奧運騎手...
function convert_text($text) {
// ...
$sourceCharset = strtoupper($this->dom->_charset); // 如 'GB2312'
$targetCharset = strtoupper($this->dom->_target_charset); // 如 'UTF-8'
if (!empty($sourceCharset) && !empty($targetCharset) && strcasecmp($sourceCharset, $targetCharset) != 0) {
// 關鍵邏輯
if ((strcasecmp($targetCharset, 'UTF-8') == 0) && ($this->is_utf8($text))) {
$converted_text = $text; // 已是 UTF-8,跳過轉換
} else {
$converted_text = iconv($sourceCharset, $targetCharset, $text); // 【問題行】
}
}
// BOM 清理...
return $converted_text;
}
simple_html_dom 通過解析 <meta charset=...> 或 HTTP Header 判斷 _charset,但網頁聲明編碼往往與實際編碼不符(如聲明 GB2312 實為 GBK/CP936,或聲明 UTF-8 實為 GB2312)。iconv('GB2312', 'UTF-8', $text) 若源數據實為 GBK,會導致部分字符(如「鏈」「鍖」)落入 GB2312 未定義區,產生錯誤或替換字符,最終出現亂碼。is_utf8() 判斷邏輯也可能誤判,導致本該轉換的數據被跳過,或本不該轉換的被強行轉換。// 直接禁用內建轉碼,交由上層業務碼統一處理 $converted_text = $text; // 替換原本的 iconv 行
$html = file_get_contents($url); // 1. 先用 mb_detect_encoding / Round-trip 法偵測真實編碼 $encoding = detect_encoding($html); // 見前文 // 2. 強制轉為 UTF-8 $html = convert_to_utf8($html); // 3. 再餵給 simple_html_dom(此時 _charset 應為 UTF-8,不觸發轉換) $dom = str_get_html($html);
loadHTML('<?xml encoding="UTF-8"?>' . $html) 指定編碼,容錯性較好。simple_html_dom 的 convert_text 過度依賴網頁聲明編碼,且 iconv 對 GB2312/GBK 混淆處理不佳。最省心的策略是:在傳給解析器前,自行完成編碼偵測與統一轉 UTF-8,並禁用庫內部轉碼。長期項目建議遷移至基於 libxml 或 HTML5 標準解析器的現代庫。