详解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 相关文章推荐
使用forever管理nodejs应用教程
Jun 03 NodeJs
基于NodeJS的前后端分离的思考与实践(一)全栈式开发
Sep 26 NodeJs
轻松创建nodejs服务器(9):实现非阻塞操作
Dec 18 NodeJs
nodejs实现HTTPS发起POST请求
Apr 23 NodeJs
Nodejs 搭建简单的Web服务器详解及实例
Nov 30 NodeJs
win系统下nodejs环境安装配置
May 04 NodeJs
nodejs项目windows下开机自启动的方法
Nov 22 NodeJs
nodejs的路径问题的解决
Jun 30 NodeJs
nodejs实现用户登录路由功能
May 22 NodeJs
浅谈使用nodejs搭建web服务器的过程
Jul 20 NodeJs
nodejs使用Sequelize框架操作数据库的实现
Oct 21 NodeJs
nodejs中的异步编程知识点详解
Jan 17 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的返回引用和局部静态变量
2015/06/04 PHP
PHP实现支持加盐的图片加密解密
2016/09/09 PHP
js检验密码强度(低中高)附图
2014/06/05 Javascript
简述JavaScript对传统文档对象模型的支持
2015/06/16 Javascript
充分发挥Node.js程序性能的一些方法介绍
2015/06/23 Javascript
JavaScript浏览器对象模型BOM(BrowserObjectModel)实例详解
2016/11/29 Javascript
基于JavaScript实现熔岩灯效果导航菜单
2017/01/04 Javascript
Bootstrap 网格系统布局详解
2017/03/19 Javascript
ES6新特性之解构、参数、模块和记号用法示例
2017/04/01 Javascript
Bootstrap.css与layDate日期选择样式起冲突的解决办法
2017/04/07 Javascript
vue2 中如何实现动态表单增删改查实例
2017/06/09 Javascript
JavaScript输入分钟、秒倒计时技巧总结(附代码)
2017/08/17 Javascript
详解vue+css3做交互特效的方法
2017/11/20 Javascript
Js经典案例的实例代码
2018/05/10 Javascript
解决cordova+vue 项目打包成APK应用遇到的问题
2019/05/10 Javascript
NodeJs 实现简单WebSocket即时通讯的示例代码
2019/08/05 NodeJs
JsonProperty 的使用方法详解
2019/10/11 Javascript
Vue两种组件类型:递归组件和动态组件的用法
2020/08/06 Javascript
[43:41]OG vs Newbee 2019国际邀请赛淘汰赛 胜者组 BO3 第一场 8.21.mp4
2020/07/19 DOTA
详解Python当中的字符串和编码
2015/04/25 Python
python装饰器与递归算法详解
2016/02/18 Python
Python实现Youku视频批量下载功能
2017/03/14 Python
实例详解Python模块decimal
2019/06/26 Python
使用python实现滑动验证码功能
2019/08/05 Python
通过python3实现投票功能代码实例
2019/09/26 Python
运行tensorflow python程序,限制对GPU和CPU的占用操作
2020/02/06 Python
老板电器官方购物商城:老板油烟机、燃气灶、消毒柜、电烤箱
2018/05/30 全球购物
美国电力供应商店/电气批发商:USESI
2018/10/12 全球购物
法国亚马逊官方网站:Amazon.fr
2020/12/19 全球购物
生产助理岗位职责
2014/06/18 职场文书
幼儿园校园小喇叭广播稿
2014/10/17 职场文书
2014年教育教学工作总结
2014/11/13 职场文书
奖励申请报告范文
2015/05/15 职场文书
运动会运动员赞词
2015/07/22 职场文书
Python 多线程处理任务实例
2021/11/07 Python
前端监听websocket消息并实时弹出(实例代码)
2021/11/27 Javascript