我们准备两个目标网站:第一个是网页编码格式为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安装相应的包。