crawler4j 抓取帶明確編碼的頁面表現優異,搭配 Jsoup 解析 HTML 更是如虎添翼。但遇到 Response Header 缺少 Content-Type charset 的頁面時,crawler4j 預設以 ISO-8859-1 解碼,導致中文內容變成亂碼,Jsoup 再接手時已無力回天。
在 crawler4j 原始碼中,Page.load(HttpEntity) 負責將回應實體轉為位元組陣列 contentData。修改此處解碼邏輯,若無編碼則預設用 GBK(或目標站常用編碼),即可從源頭修正亂碼。
public void load(HttpEntity entity) throws Exception {
contentType = null;
Header type = entity.getContentType();
if (type != null) contentType = type.getValue();
contentEncoding = null;
Header encoding = entity.getContentEncoding();
if (encoding != null) contentEncoding = encoding.getValue();
Charset charset = ContentType.getOrDefault(entity).getCharset();
if (charset != null) {
contentCharset = charset.displayName();
} else {
contentCharset = "gbk"; // ★ 關鍵:無編碼時預設 GBK
}
// 原始碼:contentData = EntityUtils.toByteArray(entity);
// 修正後:先以 GBK 解碼為字串,再獲取位元組,確保中文不亂碼
contentData = EntityUtils.toString(entity, Charset.forName("gbk")).getBytes();
}
EntityUtils.toString(entity, Charset.forName("gbk")):以 GBK 將回應體轉為 Java 字串(內部為 UTF-16)。.getBytes():將字串轉回預設編碼(通常 UTF-8)的位元組陣列,供後續 Jsoup 解析。"gbk" 替換為對應編碼名稱。Page 類)。Page.load() 為上述版本並重新編譯打包。Jsoup.parse(new String(page.getContentData())) 即可獲得正確 Document。若目標站編碼不固定,可整合 juniversalchardet 或 ICU4J 在 load() 中自動偵測:
// 伪代码
if (charset == null) {
byte[] raw = EntityUtils.toByteArray(entity);
String detected = CharsetDetector.detect(raw); // 需引入偵測庫
contentData = new String(raw, detected).getBytes();
}
一行修改徹底解決「無編碼頁面亂碼」痛點,讓 crawler4j + Jsoup 組合在中文網頁抓取場景無往不利。