nodejs爬⾍程序解决gbk等中⽂编码问题
使⽤nodejs写了⼀个爬⾍的demo,⽬的是提取⽹页的title部分。
遇到最⼤的问题就是⽹页的编码与nodejs默认编码不⼀致造成的乱码问题。nodejs⽀持utf8, ucs2, ascii, binary, base64, hex等编码⽅式,但是对于汉语⾔来说编码主要分为三种,utf-8,gb2312,gbk。这⾥⾯gbk是完全兼容gb2312的,因此在处理编码的时候主要就分为utf-8以及gbk两⼤类。(这是在没有考虑到其他国家的编码情况,⽐如⽇本的Shift_JIS编码等,同时这个iconv-lite模块⽀持的编码⽅法有限)。
⾸先说⼀下浏览器显⽰⽹页内容的时候是如何处理编码问题的。服务器和客户端进⾏通信,服务端将⽹页按照指定的编码⽅式(⽐如gbk)编码成为⼆进制码流(即我们使⽤wireshark抓包看到额16进制码流)传送给我们的客户端。客户端则会根据⽹页源码中所规定的编码⽅式,由浏览器调⽤对应的解码器,将⼆进制码流解码后显⽰出来。⽽编码⽅式通常在⽹页中是如下内容表⽰:
<meta http-equiv="Content-Type"content="text/html; charset=UTF-8"/>`或者`<meta charset=utf-8"/>
如果客户端是nodejs爬⾍请求程序,由于nodejs默认的编码⽅式是utf-8,因此爬⾍程序将接收到的⼆进制码流以字符串(默认⽅式utf-8)显⽰的时候则会显⽰乱码。这个时候需要将原始的⼆进制码流按照⽹页原来的编码⽅式解码,则不会出现乱码。
因此解决⽅法如下:
将接收到的⽹页源码以⼆进制的⽅式存储下来,处理⼆进制数据流使⽤Buffer全局对象。
<('data', function(data){
htmlData.push(data);
htmlDataLength += data.length;
});
var bufferHtmlData = at(htmlData,htmlDataLength);
然后对这些⼆进制的数据调⽤对应的解码程序。iconv-lite模块⽤于解码,cheerio模块⽤于解析⽹页内容。
decodeHtmlData = iconv.decode(bufferHtmlData,'gbk');
var $ = cheerio.load(decodeHtmlData, {decodeEntities: false});
$('title','head').each(function(i, e) {
htmlHeadTitle = $(e).text();
console.log(htmlHeadTitle);
});
上述bufferHtmlData为⼆进制码流,decodeHtmlData为将⼆进制码流通过gbk编码规则转换为unicode编码对应的数字(即usc2字节流),然后在转换为对应的字符串。下述为iconv-lite源码中解码部分,地址在:
fromEncoding: function(buf) {
buf = ensureBuffer(buf);
var idx = 0, len = 0,
newBuf = new Buffer(len*2),unicode,gbkcode;
for (var i = 0, _len = buf.length; i < _len; i++, len++) {
if (!!(buf[i] & 0x80)) {//the high bit is 1, so this byte is gbkcode's high byte.skip next byte
i++;
}
}
var newBuf = new Buffer(len*2);
for (var i = 0, j = 0, _len = buf.length; i < _len; i++, j++) {
var temp = buf[i], gbkcode, unicode;
if (temp & 0x80) {
gbkcode = (temp << 8) + buf[++i];
unicode = table[gbkcode] || iconv.defaultCharUnicode.charCodeAt(0);//not found in table, replace with defaultCharUnicode                        }else {
unicode = temp;
}
newBuf[j*2] = unicode & 0xFF;//low byte
newBuf[j*2+1] = unicode >> 8;//high byte
}
String('ucs2');
}
可以看到最终返回的是String(‘ucs2’)字符串。
爬⾍程序源码如下:
var cheerio = require('cheerio');
var http = require('http');
var iconv = require('iconv-lite');
var htmlData = [];
var htmlDataLength = 0;
var count = 0;
http.globalAgent = 'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1';
<('173', function(res) {
<('data', function(data) {
htmlData.push(data);
htmlDataLength += data.length;
count ++;
});
<('end',function(){
callback(htmlData);
});
});
function callback(htmlData){
console.log(count);
var bufferHtmlData = at(htmlData,htmlDataLength);
var charset = '';
var decodeHtmlData;
var htmlHeadTitle = '';
var htmlHeadCharset = '';
var htmlHeadContent = '';
var index = 0;
var $ = cheerio.load(bufferHtmlData, {decodeEntities: false});
$('meta','head').each(function(i, e) {
htmlHeadCharset = $(e).attr('charset');
htmlHeadContent = $(e).attr('content');
if(typeof(htmlHeadCharset) != 'undefined'){
charset = htmlHeadCharset;
}
unicode编码转换二进制if(typeof(htmlHeadContent) != 'undefined'){
if(htmlHeadContent.match(/charset=/ig)){
index = htmlHeadContent.indexOf('=');
charset = htmlHeadContent.substring(index+1);
}
}
});
//此处为什么需要对整个⽹页进⾏转吗,是因为cheerio这个组件不能够返回buffer,iconv则⽆法转换之if(charset.match(/gb/ig)){
decodeHtmlData = iconv.decode(bufferHtmlData,'gbk');
}
else{//因为有可能返回的⽹页中不存在charset字段,因此默认都是按照utf8进⾏处理
decodeHtmlData = iconv.decode(bufferHtmlData,'utf8');
}
var $ = cheerio.load(decodeHtmlData, {decodeEntities: false});
$('title','head').each(function(i, e) {
htmlHeadTitle = $(e).text();
console.log(htmlHeadTitle);
});
console.log(charset);
}
本⽂为CSDN村中少年原创⽂章,转载记得加上⼩尾巴偶,博主链接。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。