详解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中简单实现Javascript Promise机制的实例
Dec 06 NodeJs
nodejs和php实现图片访问实时处理
Jan 05 NodeJs
详解nodejs中exports和module.exports的区别
Feb 17 NodeJs
Nodejs 获取时间加手机标识的32位标识实现代码
Mar 07 NodeJs
实例分析nodejs模块xml2js解析xml过程中遇到的坑
Mar 18 NodeJs
详解NodeJS框架express的路径映射(路由)功能及控制
Mar 24 NodeJs
CentOS 安装NodeJS V8.0.0的方法
Jun 15 NodeJs
nodejs密码加密中生成随机数的实例代码
Jul 17 NodeJs
nodejs实现爬取网站图片功能
Dec 14 NodeJs
基于nodejs res.end和res.send的区别
May 14 NodeJs
基于Nodejs的Tcp封包和解包的理解
Sep 19 NodeJs
nodejs中使用worker_threads来创建新的线程的方法
Jan 22 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 引用(&amp;)详解
2009/11/20 PHP
怎样给PHP源代码加密?PHP二进制加密与解密的解决办法
2013/04/22 PHP
laravel 中如何使用ajax和vue总结
2017/08/16 PHP
TP框架实现上传一张图片和批量上传图片的方法分析
2020/04/23 PHP
JS中字符问题(二进制/十进制/十六进制及ASCII码之间的转换)
2008/11/03 Javascript
Javascript面向对象编程(二) 构造函数的继承
2011/08/28 Javascript
web开发人员学习jQuery的6大理由及jQuery的优势介绍
2013/01/03 Javascript
javascript实现动态侧边栏代码
2014/02/19 Javascript
JS获取网页属性包括宽、高等等
2014/04/03 Javascript
EasyUI实现二级页面的内容勾选的方法
2015/03/01 Javascript
js判断图片加载完成后获取图片实际宽高的方法
2016/02/25 Javascript
使用jquery获取url以及jquery获取url参数的实现方法
2016/05/25 Javascript
jQuery中Chosen三级联动功能实例代码
2017/03/07 Javascript
微信小程序 判断手机号的实现代码
2017/04/19 Javascript
使用veloticy-ui生成文字动画效果
2018/02/08 Javascript
详解Vue前端对axios的封装和使用
2019/04/01 Javascript
详解Vue中组件的缓存
2019/04/20 Javascript
JS 5种遍历对象的方式
2020/06/16 Javascript
微信小程序自定义modal弹窗组件的方法详解
2020/12/20 Javascript
Linux下Python获取IP地址的代码
2014/11/30 Python
对python实现模板生成脚本的方法详解
2019/01/30 Python
Python爬虫beautifulsoup4常用的解析方法总结
2019/02/25 Python
Django 对IP访问频率进行限制的例子
2019/08/30 Python
在pycharm中debug 实时查看数据操作(交互式)
2020/06/09 Python
python 基于opencv实现高斯平滑
2020/12/18 Python
HTML5视频播放插件 video.js介绍
2018/09/29 HTML / CSS
美国睫毛、眉毛精华液领导品牌:RevitaLash Cosmetics
2018/03/26 全球购物
专科毕业生学习生活的自我评价
2013/10/26 职场文书
安全生产中长期规划实施方案
2014/02/21 职场文书
办理房产证委托书
2014/09/18 职场文书
学校施工安全责任书
2015/01/29 职场文书
2019新员工试用期转正工作总结范文
2019/08/21 职场文书
2020年个人安全保证书参考模板
2020/01/08 职场文书
Python-typing: 类型标注与支持 Any类型详解
2021/05/10 Python
HTML5 语义化标签(移动端必备)
2021/08/23 HTML / CSS
MySQL分库分表详情
2021/09/25 MySQL