之前在用 node 做爬蟲時碰到的中文亂碼問題一直沒有解決,今天整理下備忘。(PS:網上一些解決方案都已經不行了)
中文亂碼具體是指用 node 請求 gbk 編碼的網頁,無法正確獲取網頁中的中文(需要轉碼),"gbk" 和 "網頁中的中文" 兩個條件是缺一不可的。可以獲取 utf-8 編碼的網頁中的中文,也可以獲取 gbk 編碼網頁中的英文數字等。
舉個簡單的例子。獲取 http://acm.hdu.edu.cn/statistic.php?pid=1000 排名第一的答案的 username,是為 "極光炫影"。刷刷刷寫下如下代碼:
var cheerio = require('cheerio') , superagent = require('superagent') , express = require('express'); var url = 'http://acm.hdu.edu.cn/statistic.php?pid=1000'; var app = express(); app.get('/', function (req, res, next) { superagent.get(url) .end(function (err, sres) { var html = sres.text; var $ = cheerio.load(html, {decodeEntities: false}); var ans = $('.table_text td a').eq(0).html(); res.send(ans); }); }); app.listen(3000, function () { console.log('app is listening at port 3000'); });
得到了亂碼,如下:
������Ӱ
如何獲取正確的中文呢?這里提供幾個解決方案應急(不關心原理,只是為了應急)。
方法一:
使用 superagent-charset 模塊。
var cheerio = require('cheerio') , superagent = require('superagent-charset') , express = require('express'); var url = 'http://acm.hdu.edu.cn/statistic.php?pid=1000'; var app = express(); app.get('/', function (req, res, next) { superagent.get(url) .charset('gbk') .end(function (err, sres) { var html = sres.text; var $ = cheerio.load(html, {decodeEntities: false}); var ans = $('.table_text td a').eq(0).html(); res.send(ans); }); }); app.listen(3000, function () { console.log('app is listening at port 3000'); });
使用非常簡單,只需要引入 superagent-charset
模塊,且在鏈式調用時加入 charset 參數即可。superagent-charset 模塊包括了 superAgent 模塊以及 iconv-lite 模塊。源碼可以參考 Github 。
方法二:
直接用 iconv-lite 模塊進行轉碼。
iconv-lite 是一個進行編碼轉換的模塊(node 默認編碼 utf-8)。需要 decode 的編碼必須是 Buffer 類型。
-
用
http
模塊:http.get(url, function(sres) { var chunks = []; sres.on('data', function(chunk) { chunks.push(chunk); }); sres.on('end', function() { // 將二進制數據解碼成 gb2312 編碼數據 var html = iconv.decode(Buffer.concat(chunks), 'gb2312'); var $ = cheerio.load(html, {decodeEntities: false}); var ans = $('.table_text td a').eq(0).html(); res.send(ans); }); });
-
用 request 模塊:
request({ url: url, encoding: null // 關鍵代碼 }, function (err, sres, body) { var html = iconv.decode(body, 'gb2312') var $ = cheerio.load(html, {decodeEntities: false}); var ans = $('.table_text td a').eq(0).html(); res.send(ans); });
用 iconv 進行 decode 傳入的參數必須是 Buffer。
encoding
- Encoding to be used on setEncoding ofresponse
data. Ifnull
, thebody
is returned as aBuffer
. Anything else ( including the default value ofundefined
) will be passed as the encoding parameter totoString()
(meaning this is effectivelyutf8
by default). ( Note : if you expect binary data, you should setencoding: null
.)
iconv-lite 模塊能配合 http 模塊以及 request 模塊使用,卻不能直接和 superAgent 模塊使用。 因為 superAgent 是以 utf8 去取數據,然后再用 iconv 轉也是不行的。 頁面是 gbk 編碼的,sres.text 已經是 decode 過了的結果,也就是說它已經被轉換成 utf8 了,再轉換成 buffer 出來的結果必須是不正確的。