详解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 相关文章推荐
基于NodeJS的前后端分离的思考与实践(六)Nginx + Node.js + Java 的软件栈部署实践
Sep 26 NodeJs
轻松创建nodejs服务器(7):阻塞操作的实现
Dec 18 NodeJs
Nodejs为什么选择javascript为载体语言
Jan 13 NodeJs
深入浅析NodeJs并发异步的回调处理
Dec 21 NodeJs
Ajax异步文件上传与NodeJS express服务端处理
Apr 01 NodeJs
NodeJs使用Mysql模块实现事务处理实例
May 31 NodeJs
Nodejs实现多房间简易聊天室功能
Jun 20 NodeJs
nodejs中sleep功能实现暂停几秒的方法
Jul 12 NodeJs
nodejs爬虫初试superagent和cheerio
Mar 05 NodeJs
nodejs连接mysql数据库及基本知识点详解
Mar 20 NodeJs
nodejs检测因特网是否断开的解决方案
Apr 17 NodeJs
nodejs搭建本地服务器并访问文件操作示例
May 11 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 超链接 抓取实现代码
2009/06/29 PHP
PHP函数preg_match_all正则表达式的基本使用详细解析
2013/08/31 PHP
在Win2003(64位)中配置IIS6+PHP5.2.17+MySQL5.5的运行环境
2016/04/04 PHP
使用ThinkPHP生成缩略图及显示
2017/04/27 PHP
PHP 对象继承原理与简单用法示例
2020/04/21 PHP
读jQuery之十一 添加事件核心方法
2011/07/31 Javascript
JavaScript之自定义类型
2012/05/04 Javascript
js判断文本框输入的内容是否为数字
2015/12/23 Javascript
JS+CSS3制作炫酷的弹窗效果
2016/11/08 Javascript
localStorage的黑科技-js和css缓存机制
2017/02/06 Javascript
Vue自定义指令封装节流函数的方法示例
2018/07/09 Javascript
Vue slot用法(小结)
2018/10/22 Javascript
iview在vue-cli3如何按需加载的方法
2018/10/31 Javascript
JavaScript栈和队列相关操作与实现方法详解
2018/12/07 Javascript
详解Vue源码中一些util函数
2019/04/24 Javascript
vue项目中在外部js文件中直接调用vue实例的方法比如说this
2019/04/28 Javascript
JS实现的简单tab切换功能完整示例
2019/06/20 Javascript
Vue 使用typescript如何优雅的调用swagger API
2020/09/01 Javascript
JavaScript中的几种继承方法示例
2020/12/06 Javascript
[27:53]2014 DOTA2华西杯精英邀请赛 5 24 NewBee VS iG
2014/05/26 DOTA
[01:42:49]DOTA2-DPC中国联赛 正赛 iG vs PSG.LGD BO3 第一场 2月26日
2021/03/11 DOTA
Python中threading模块join函数用法实例分析
2015/06/04 Python
python列表操作之extend和append的区别实例分析
2015/07/28 Python
解决PyCharm的Python.exe已经停止工作的问题
2018/11/29 Python
详解如何在cmd命令窗口中搭建简单的python开发环境
2019/08/29 Python
python程序文件扩展名知识点详解
2020/02/27 Python
python GUI库图形界面开发之PyQt5工具栏控件QToolBar的详细使用方法与实例
2020/02/28 Python
都柏林通行卡/城市通票:The Dublin Pass
2020/02/16 全球购物
大专生工程监理求职信
2013/10/04 职场文书
中学校庆方案
2014/03/17 职场文书
公司经理任命书
2014/06/05 职场文书
捐助倡议书
2015/01/19 职场文书
2016大一新生入学教育心得体会
2016/01/23 职场文书
Python matplotlib安装以及实现简单曲线的绘制
2022/04/26 Python
德生TECSUN S-2000使用手册文字版
2022/05/10 无线电
MySQL外键约束(Foreign Key)案例详解
2022/06/28 MySQL