详解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 相关文章推荐
Ubuntu中搭建Nodejs开发环境过程分享
Jun 01 NodeJs
使用forever管理nodejs应用教程
Jun 03 NodeJs
Nodejs极简入门教程(一):模块机制
Oct 25 NodeJs
NodeJS学习笔记之MongoDB模块
Jan 13 NodeJs
配置nodejs环境的方法
May 13 NodeJs
使用Nodejs连接mongodb数据库的实现代码
Aug 21 NodeJs
nodejs简单实现TCP服务器端和客户端的聊天功能示例
Jan 04 NodeJs
nodejs acl的用户权限管理详解
Mar 14 NodeJs
基于Koa(nodejs框架)对json文件进行增删改查的示例代码
Feb 02 NodeJs
nodejs分离html文件里面的js和css的方法
Apr 09 NodeJs
nodejs搭建本地服务器并访问文件操作示例
May 11 NodeJs
nodejs语言实现验证码生成功能的示例代码
Oct 13 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
PHPMyAdmin 快速配置方法
2009/05/11 PHP
PHP 采集程序原理分析篇
2010/03/05 PHP
php基于单例模式封装mysql类完整实例
2016/10/18 PHP
PHP大文件分割上传 PHP分片上传
2017/08/28 PHP
Laravel+Intervention实现上传图片功能示例
2019/07/09 PHP
Code: write(s,d) 输出连续字符串
2007/08/19 Javascript
javascript中的new使用
2010/03/20 Javascript
简单的前端js+ajax 购物车框架(入门篇)
2011/10/29 Javascript
onmouseover和onmouseout的一些问题思考
2013/08/14 Javascript
jQuery中unwrap()方法用法实例
2015/01/16 Javascript
详解如何实现一个简单的Node.js脚手架
2017/12/04 Javascript
Koa2微信公众号开发之本地开发调试环境搭建
2018/05/16 Javascript
微信小程序自定义音乐进度条的实例代码
2018/08/28 Javascript
vue 项目地址去掉 #的方法
2018/10/20 Javascript
京东优选小程序的实现代码示例
2020/02/25 Javascript
解决vue页面刷新,数据丢失的问题
2020/11/24 Vue.js
python解析html开发库pyquery使用方法
2014/02/07 Python
python海龟绘图实例教程
2014/07/24 Python
python获取本地计算机名字的方法
2015/04/29 Python
详解Numpy数组转置的三种方法T、transpose、swapaxes
2019/05/27 Python
Python中变量的输入输出实例代码详解
2019/07/28 Python
Python将QQ聊天记录生成词云的示例代码
2021/02/10 Python
一款利用纯css3实现的win8加载动画的实例分析
2014/12/11 HTML / CSS
美国沃尔玛网上超市:Walmart
2020/08/14 全球购物
毕业实习个人鉴定范文
2013/12/10 职场文书
创业计划书中要认真思考的问题
2013/12/28 职场文书
行政专员求职信范文
2014/05/03 职场文书
群众路线教育实践活动批评与自我批评
2014/09/15 职场文书
个人四风对照检查材料
2014/09/26 职场文书
个人向公司借款协议书
2014/10/09 职场文书
2015年中学校长工作总结
2015/05/19 职场文书
罗马假日观后感
2015/06/08 职场文书
2015暑期爱心支教策划书
2015/07/14 职场文书
解析目标检测之IoU
2021/06/26 Python
Vue如何清空对象
2022/03/03 Vue.js
DE1103使用报告
2022/04/05 无线电