详解nodejs爬虫程序解决gbk等中文编码问题


Posted in NodeJs onApril 06, 2017

使用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全局对象。

res.on('data', function(data) {

  htmlData.push(data);
  htmlDataLength += data.length;
 });
var bufferHtmlData = Buffer.concat(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
          }
          return newBuf.toString('ucs2');
        }

可以看到最终返回的是newBuf.toString(‘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';
http.get('http://www.cr173.com', function(res) {

 res.on('data', function(data) {

  htmlData.push(data);
  htmlDataLength += data.length;
  count ++;
 });

 res.on('end',function(){

  callback(htmlData);
 });

});

function callback(htmlData){

 console.log(count);
 var bufferHtmlData = Buffer.concat(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;
  }

  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);

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

NodeJs 相关文章推荐
14款NodeJS Web框架推荐
Jul 11 NodeJs
Nodejs中调用系统命令、Shell脚本和Python脚本的方法和实例
Jan 01 NodeJs
NodeJS学习笔记之Http模块
Jan 13 NodeJs
Nodejs实战心得之eventproxy模块控制并发
Oct 27 NodeJs
nodejs微信公众号支付开发
Sep 19 NodeJs
nodejs redis 发布订阅机制封装实现方法及实例代码
Dec 15 NodeJs
async/await与promise(nodejs中的异步操作问题)
Mar 03 NodeJs
NodeJS创建最简单的HTTP服务器
May 15 NodeJs
Nodejs之TCP服务端与客户端聊天程序详解
Jul 07 NodeJs
Nodejs+express+ejs简单使用实例代码
Sep 18 NodeJs
nodejs搭建本地服务器轻松解决跨域问题
Mar 21 NodeJs
NodeJS基础API搭建服务器详细过程记录
Apr 01 #NodeJs
Ajax异步文件上传与NodeJS express服务端处理
Apr 01 #NodeJs
3分钟快速搭建nodejs本地服务器方法运行测试html/js
Apr 01 #NodeJs
nodejs使用express创建一个简单web应用
Mar 31 #NodeJs
nodejs实现邮件发送服务实例分享
Mar 29 #NodeJs
NodeJs测试框架Mocha的安装与使用
Mar 28 #NodeJs
NodeJS测试框架mocha入门教程
Mar 28 #NodeJs
You might like
利用PHP制作简单的内容采集器的代码
2007/11/28 PHP
PHP生成各种常见验证码和Ajax验证过程
2016/01/10 PHP
Symfony2框架创建项目与模板设置实例详解
2016/03/17 PHP
Yii实现Command任务处理的方法详解
2016/07/14 PHP
PHP中检查isset()和!empty()函数的必要性
2019/02/13 PHP
thinkphp5实现无限级分类
2019/02/18 PHP
JS 显示当前日期与时间的代码
2010/03/24 Javascript
关于JS控制代码暂停的实现方法分享
2012/10/11 Javascript
基于JavaScript实现继承机制之构造函数方法对象冒充的使用详解
2013/05/07 Javascript
js函数名与form表单元素同名冲突的问题
2014/03/07 Javascript
jQuery的css()方法用法实例
2014/12/24 Javascript
jQuery中scrollLeft()方法用法实例
2015/01/16 Javascript
Node.js编写组件的三种实现方式
2016/02/25 Javascript
Node.JS中事件轮询(Event Loop)的解析
2017/02/25 Javascript
jQuery中ajax获取数据赋值给页面的实例
2017/12/31 jQuery
javascript原型链学习记录之继承实现方式分析
2019/05/01 Javascript
jquery ui 实现 tab标签功能示例【测试可用】
2019/07/25 jQuery
js模拟F11页面全屏显示
2019/09/17 Javascript
js制作提示框插件
2020/12/24 Javascript
Python中的zipfile模块使用详解
2015/06/25 Python
简要讲解Python编程中线程的创建与锁的使用
2016/02/28 Python
Python如何生成树形图案
2018/01/03 Python
python中requests和https使用简单示例
2018/01/18 Python
python在文本开头插入一行的实例
2018/05/02 Python
python递归函数求n的阶乘,优缺点及递归次数设置方式
2020/04/02 Python
用60行代码实现Python自动抢微信红包
2021/02/04 Python
python中numpy.empty()函数实例讲解
2021/02/05 Python
沪江旗下的海量优质课程平台:沪江网校
2017/11/07 全球购物
什么是URL
2015/12/13 面试题
如何反序的迭代一个序列?how do I iterate over a sequence in reverse order
2012/02/04 面试题
监察建议书范文
2014/03/12 职场文书
感恩母亲节演讲稿
2014/05/07 职场文书
停车场管理协议书范本
2014/10/08 职场文书
庆祝三八妇女节标语
2014/10/09 职场文书
乡镇群众路线专项整治方案
2014/11/03 职场文书
导游词之阳朔遇龙河
2019/12/16 职场文书