node爬虫之gbk网页中文乱码解决方案

  • A+
所属分类:Node.js

之前在用 node 做爬虫时碰到的中文乱码问题一直没有解决,今天整理下备忘。(PS:网上一些解决方案都已经不行了)

中文乱码具体是指用 node 请求 gbk 编码的网页,无法正确获取网页中的中文(需要转码),"gbk" 和 "网页中的中文" 两个条件是缺一不可的。可以获取 utf-8 编码的网页中的中文,也可以获取 gbk 编码网页中的英文数字等。

举个简单的例子。获取  search.51job.com  招聘信息 代码如下:

var http = require('http');
var cheerio = require('cheerio');
var fs = require('fs');

var options = {
  hostname:'search.51job.com',
  port: 80,
  path: '/list/020000,000000,0000,00,9,99,Web%25E5%2589%258D%25E7%25AB%25AF%25E5%25BC%2580%25E5%258F%2591,2,1.html?lang=c&stype=&postchannel=0000&workyear=99&cotype=99&degreefrom=99&jobterm=99&companysize=99&providesalary=99&lonlat=0%2C0&radius=-1&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare=',
  method: 'GET',
  headers: {
    'Connection' : 'keep-alive',
    'Pragma' : 'no-cache',
    'Cache-Control' : 'no-cache',
    'Upgrade-Insecure-Requests' : '1',
    'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36',
    'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Referer' : 'http://www.51job.com/?from=baidupz',
    'Accept-Language' : 'zh-CN,zh;q=0.8,en;q=0.6'
  }
}

var req = http.request(options, function (res) {
  var arr = [];
  res.on('data', function(chunk) {
    arr.push(chunk);
  });
  res.on('end', function(){
    var buffer = Buffer.concat(arr);
    var html = buffer.toString();
    var jsons = [];
    var $ = cheerio.load(html);
    $('div.el').each(function(i,v) {
      var doc = {
        position: $(v).find('.t1 span a').text().trim(),
        company: $(v).find('.t2 a').text(),
        region: $(v).find('.t3').text(),
        salary: $(v).find('.t4').text(),
        time: $(v).find('t5').text()
      }
      jsons.push(doc);
    })
    fs.writeFile('./data.json', JSON.stringify(jsons),function(err) {
      if(err) {
        throw err;
      }
      console.log('ok');
    })
  });
});

req.on('error', function(err) {
  console.log(err);
})

req.end();

得到了乱码,如下:

������Ӱ

如何获取正确的中文呢?

直接用 iconv-lite 模块进行转码。

iconv-lite 是一个进行编码转换的模块(node 默认编码 utf-8)。需要 decode 的编码必须是 Buffer类型。

var http = require('http');
var cheerio = require('cheerio');
var iconv = require('iconv-lite'); 
var fs = require('fs');

var options = {
  hostname:'search.51job.com',
  port: 80,
  path: '/list/020000,000000,0000,00,9,99,Web%25E5%2589%258D%25E7%25AB%25AF%25E5%25BC%2580%25E5%258F%2591,2,1.html?lang=c&stype=&postchannel=0000&workyear=99&cotype=99&degreefrom=99&jobterm=99&companysize=99&providesalary=99&lonlat=0%2C0&radius=-1&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare=',
  method: 'GET',
  headers: {
    'Connection' : 'keep-alive',
    'Pragma' : 'no-cache',
    'Cache-Control' : 'no-cache',
    'Upgrade-Insecure-Requests' : '1',
    'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36',
    'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Referer' : 'http://www.51job.com/?from=baidupz',
    'Accept-Language' : 'zh-CN,zh;q=0.8,en;q=0.6'
  }
}

var req = http.request(options, function (res) {
  var arr = [];
  res.on('data', function(chunk) {
    arr.push(chunk);
  });
  res.on('end', function(){
    var html = iconv.decode(Buffer.concat(arr), 'gbk');
    var jsons = [];
    var $ = cheerio.load(html, {decodeEntities: false});
    $('div.el').each(function(i,v) {
      var doc = {
        position: $(v).find('.t1 span a').text().trim(),
        company: $(v).find('.t2 a').text(),
        region: $(v).find('.t3').text(),
        salary: $(v).find('.t4').text(),
        time: $(v).find('t5').text()
      }
      jsons.push(doc);
    })
    fs.writeFile('./data.json', JSON.stringify(jsons),function(err) {
      if(err) {
        throw err;
      }
      console.log('ok');
    })
  });
});

req.on('error', function(err) {
  console.log(err);
})

req.end();
  • 用 iconv 进行 decode 传入的参数必须是 Buffer。

    encoding - Encoding to be used on setEncoding of response data. If null, the body is returned as a Buffer. Anything else (including the default value of undefined) will be passed as the encoding parameter to toString() (meaning this is effectively utf8 by default). (Note: if you expect binary data, you should set encoding: null.)

iconv-lite 模块能配合 http 模块以及 request 模块使用,却不能直接和 superAgent 模块使用。因为 superAgent 是以 utf8 去取数据,然后再用 iconv 转也是不行的。页面是 gbk 编码的,sres.text 已经是 decode 过了的结果,也就是说它已经被转换成 utf8 了,再转换成 buffer 出来的结果必须是不正确的。

weinxin
我的微信
爱生活、爱学习的小伙伴可以通过扫一扫二维码添加我的个人微信一起交流!
青青子衿

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: