node.js jsdom gb系列网页中文乱码问题解决方案

来源:互联网 发布:美国知乎评论中国 编辑:程序博客网 时间:2024/04/29 21:01
博客搬家:由于各种原因,我现在的博客将首发于blog.mojijs.com, 可以百度搜索 “姜哥的墨迹技术博客” , 或者 点击这里 本文地址 http://blog.mojijs.com/post/14.html
最近使用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。
原创粉丝点击