Email:longsu2010 at yeah dot net
最近使用node.js写点东西,使用到了jsdom。使用过程中遇到解析GBK或者GB2312编码网页乱码的问题。下面以"http://www.w3school.com.cn"(网页编码gb2312)为例讲解乱码问题及我的应对方案。
如下代码是获取id为w3的dom节点并打印该节点的innerHTML属性,由于其中含有中文,所以会输出许多个问号。
var jsdom = require("jsdom");
var fs = require("fs");
var jquery = fs.readFileSync(__dirname + "/lib/jquery.js").toString();
jsdom.env({
html : "http://www.w3school.com.cn/",
src : [jquery],
done : function (errors, window) {
var $ = window.$;
console.log( $("#w3")[0].innerHTML );
}
});
我想jsdom的env必定会有一个配置项可以设置需要使用什么编码,简单查了一下文档没发现这方面的信息,绝招是看源码(开源就是好)。在源码中发现jsdom使用了request模块,而request可以配置一个encoding配置项来指定编码。request有一这样一个特性,若encoding设置为null,那么request返回的是一个Buffer(这点很重要)。看看jsdom中是怎么写的吧,如下:
request({
uri : url,
encoding : config.encoding || 'utf8',
headers : config.headers || {},
proxy : config.proxy || null
}, function(err, request, body) {
processHTML(err, body);
});
看了这段代码我就明白了,在调用jsdom的env方法的时候你给encoding传null没用,他会使用utf8。所以我把这段代码改成了如下这样:
request({
uri : url,
encoding : (typeof(config.encoding) === "undefined") ? 'utf8' : config.encoding,
headers : config.headers || {},
proxy : config.proxy || null
}, function(err, request, body) {
processHTML(err, body);
});
这样jsdom使用request模块就可以得到一个Buffer。这还没完,因为jsdom不会给request传递encoding为null的配置项,所以jsdom不会接受到Buffer,因此jsdom中很可能没处理返回值是Buffer的情况。根据上述代码片段可知request的回调函数是processHTML,那就是说返回值处理在processHTML中,代码如下:
processHTML = function(err, html) {
html += '';
if(err) {
return callback(err);
}
// 此处省略其他代码
}
看了代码我知道我想错了,人家还真处理了,很通用的一句代码html += '',如果html是Buffer那么这句代码相当于html = html.toString(),而Buffer的toString方法作用正是取Buffer中字符串的,参数为编码方式,默认是utf8。这就不成了,网页是gb2312的。于是我做了如下修改:
processHTML = function(err, html) {
if(err) {
return callback(err);
}
if(html instanceof Buffer){
html = iconvLite.decode(html, config.decoding)
}
html += '';
// 此处省略其他代码
}
如上用到了iconv-lite模块,需要到jsdom中引入。decoding参数是我自己后加的,意义为解码的方式。所以jsdom的env方法调用方式如下即可解决中文乱码。
jsdom.env({
html : "http://www.w3school.com.cn/",
src : [jquery],
encoding : null,
decoding : "GBK",
done : function (errors, window) {
var $ = window.$;
console.log( $("#w3")[0].innerHTML );
}
});
到这里就结束了,有两个地方需要说明。
1、回想一下Buffer的toString方法参数默认是utf8,那我们传GBK不就成了么?其实还真不成,详情请阅读node.js关于Buffer的文档。
2、GBK兼容GB2312,所以可以使用GBK搞定GB2312。