Nodejs编写爬虫处理乱码详解

当我们用nodejs编写爬虫向目标网站爬取网页时,目标网站的编码格式可能不是utf8格式的,而在nodejs中大部分处理数据的api默认都是用utf8,所以这种情况下就会出现乱码。下面笔者将通过不同的例子来演示请求结果出现乱码的各种情况,并解决。

我们准备两个目标网站:第一个是网页编码格式为utf8的百度https://www.baidu.com/,如何查看目标网站的编码格式呢?只需要查看查看网页源代码就可以了,如图:

另外一个网站为:http://www.biqugew.com/book/15/,编码格式为gbk,如图:

首先我们用nodejs的http模块分别尝试去请求这两个网站,看看得到什么结果,首先我们用http模块请求百度,代码如下:

const http = require('http');
let options = {
    host:'www.baidu.com',
    port:80
};
let req = http.request(options,(res)=>{
    let info = '';
    res.on('data',(chunk)=>{
        info+=chunk;
    })
    res.on('end',()=>{
        console.log(info);
    })
})
req.end();

打印结果为:

可以看到结果正常打印不是乱码。

下面将url地址换成上面那个编码格式为gbk的网站,代码为:

const http = require('http');
let options = {
    host:'www.biqugew.com',
    port:80,
    path:'/book/15/'
};
let req = http.request(options,(res)=>{
    let info = '';
    res.on('data',(chunk)=>{
        info+=chunk;
    })
    res.on('end',()=>{
        console.log(info);
    })
})
req.end();

打印结果为:

出现乱码了,为什么会出现乱码呢,请求百度的页面不会出现乱码,请求这个网站就会出现乱码,what the fuck? 问题出在哪里呢?

接下来看一段代码:

var buf1= Buffer.from('哈哈哈')
var buf2= Buffer.from('嘿嘿嘿')
console.log(buf1)  //打印出来的是个buffer
console.log(buf2)  //打印出来的是个buffer
var str =  buf1+buf2;   //打印出了一个字符串
//两个buffer拼接打印出来的却是一个字符串
// 这里两个buffer拼接得到的不是一个buffer而是一个字符串,
// 为什么会这样呢,原来buffer通过“+”相连会被隐式转换为字符串,
// 并且是utf8编码格式的
console.log(str,213233123321)

运行结果如图:

可以看到用加号(+)连接buffer会对其进行隐式转换,并且默认是按照utf8的格式转化的,所以我们不能用+拼接buffer,为什么会插入这样一个知识点呢?因为我们在上面的代码中就是这么干的,并且通过data事件得到的chunk就是buffer,这个buffer的编码格式与目标网站的编码格式相同。

所以请求百度的时候,百度的编码格式为uft8,用加号连接buffer默认会转化成utf8格式额字符串,而请求另一个GBK编码的网站得到的buffer是gbk格式的,此时用加号拼接buffer,还是按照默认编码格式utf8解析就会出现乱码。

那该怎么办呢?解决方案就是我们通过data事件得到所有返回的buffer,然后根据buffer相应的编码格式将其解析,得到响应内容。那这里面就包含了两个问题,1、如何拼接buffer而不会对其进行隐式转换;2、如何将buffer按照其编码格式进行解析。

首先是第一点,拼接buffer不能用+,那要怎么拼接呢,看代码:

var buf1= Buffer.from('哈哈哈')
var buf2= Buffer.from('嘿嘿嘿')
var buf3 = [buf1,buf2]
let chunkbuffer = Buffer.concat(buf3,buf1.length+buf2.length);
console.log(chunkbuffer.toString())

执行结果:

得到的是buffer,而不是字符串。

代码解释:首先定义了两个buffer,将这两个buffer放到数组中,然后调用Buffer类的concat方法拼接buffer,这个方法接受两个参数,第一个参数为要拼接的buffer的数组,第二个参数为要拼接的buffer的总长度。第二个参数可以省略,一般会带上,这样会提高性能。

将请求gbk编码格式网站的代码修改如下:

const http = require('http');
let options = {
    host:'www.biqugew.com',
    port:80,
    path:'/book/15/'
};
let req = http.request(options,(res)=>{
    let arr = [];
    res.on('data',(chunk)=>{
        arr.push(chunk)
    })
    res.on('end',()=>{
       let chunkall = Buffer.concat(arr);
       console.log(chunkall)
    })
})
req.end();

执行结果为:

完美的得到了buffer,但是这个buffer是gbk格式的,如何将gbk格式的buffer转化为字符串呢?

这里就需要用到一个npm包iconv-lite。这个包主要提供了两个方法decode和encode。decode方法解码,将buffer按照其编码格式解码输出字符串。encode将字符串转换成指定类型的buffer。

将iconv-lite应用到代码中,代码如下:

const http = require('http');
const iconv = require('iconv-lite');
let options = {
    host:'www.biqugew.com',
    port:80,
    path:'/book/15/'
};
let req = http.request(options,(res)=>{
    let arr = [];
    res.on('data',(chunk)=>{
        arr.push(chunk)
    })
    res.on('end',()=>{
       let chunkall = Buffer.concat(arr);
       let strall=iconv.decode(chunkall,'gbk')
       console.log(strall)
    })
})
req.end();

执行结果为:

用nodejs做网页爬虫最常用的库就是request了,用这个库爬取回来的网页数据会默认按照utf8编码格式解析,所以要对这个库进行一下设置,将其options参数中的encoding设置为null,测试代码如下:

var iconv = require('iconv-lite');
var request = require('request');

request({
    method: 'GET',
    uri: 'http://www.biqugew.com',
    encoding:null

}, function (error, response, body) {
    console.log(response.body)
    let gbkstr = iconv.decode(response.body,'gb2312');
    console.log(gbkstr)
})

原理已经介绍的差不多了,nodejs中做爬虫还有很多包,这里就不一一介绍了,只要能得到相应的buffer,并且知道目标网站的编码格式,将buffer按照其编码格式转换为字符串就可以了。

有兴趣的同学可以直接复制黏贴代码测试一下,别忘了用npm安装相应的包。

原文发布于微信公众号 - nodejs全栈开发(geekclass)

原文发表时间:2018-02-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券