之前在用 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 模塊(2016-08-26:如出錯,請使用 0.1.1 版本,安裝命令 npm install superagent-charset@0.1.1 --save 或者直接在 package.json 中修改,因為之后的版本修改了 api,以下的代碼是針對 0.1.1 的 api,有空我修改下)。
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 ofresponsedata. Ifnull, thebodyis returned as aBuffer. Anything else (including the default value ofundefined) will be passed as the encoding parameter totoString()(meaning this is effectivelyutf8by 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 出來的結果必須是不正確的。
Read More:
