日期:2014-05-16 浏览次数:20329 次
最近使用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。