前言:
本文介绍了如何从零开始开发一个nodejs爬虫,以及如何使用node-crawler来实现一个爬虫。
1.Robots协议
存放于网站根目录的文件,定义了哪些目录可以抓取,哪些目录禁止抓取。
举个例子中国天气网
访问:域名+'/robots.txt',http://www.weather.com.cn/robots.txt
User-agent:?*
Allow:?/
可以看到,任意用户代理,允许爬取所有
2.开始前准备两个库
- superagent(客户端请求代理)
- cheerio(nodejs中的jquery)
3.superagent库的使用
- 安装
npm?install?superagent
- 使用
const?superagent?=?require('superagent');?//客户端代理,用来发送get、post等请求
let?BASE_URL?=?"https://movie.douban.com/cinema/nowplaying/shijiazhuang/";//要爬取的地址
superagent.get(BASE_URL)
.end((err,?res)?=>?{
?if?(err)?{
??return?console.error(err);
?}
?console.log(res)?//这里是爬到的内容
});
//输出:
{
??statusCode:?200,
??status:?200,
??statusType:?2,
??info:?false,
??ok:?true,
??redirect:?false,
??clientError:?false,
??serverError:?false,
??error:?false,
??created:?false,
??accepted:?false,
??noContent:?false,
??badRequest:?false,
??unauthorized:?false,
??notAcceptable:?false,
??forbidden:?false,
??notFound:?false,
??unprocessableEntity:?false,
??type:?'text/html',
??charset:?'utf-8',
??links:?{},
??setEncoding:?[Function:?bound?],
??redirects:?[],
??pipe:?[Function],
??[Symbol(kCapture)]:?false,
??text:{
??????//内容为爬到的html,太多了,暂时略去,这块内容也是我们接下来要处理的
??}
}
4.cheerio库
- nodejs中的jquery,用来处理爬到的html,从中筛选我们需要的数据
//从爬取到的数据中筛选出正在热映的影片信息
const?superagent?=?require('superagent');
const?cheerio?=?require('cheerio');?
let?BASE_URL?=?"https://movie.douban.com/cinema/nowplaying/shijiazhuang/";
superagent.get(BASE_URL)
.end((err,?res)?=>?{
?if?(err)?{
??return?console.error(err);
?}
?let?$?=?cheerio.load(res.text);?//载入我们要处理的数据
?let?arr?=?[];//创建一个数组,用来存储我们处理完的数据
?
?//这里我们通过分析爬取到的html,找到我们要
?$('#nowplaying?.lists?.list-item').each((index,?item)?=>?{
??let?title?=?$(item).find('.stitle?.ticket-btn').attr('title');
??let?score?=?$(item).find('.subject-rate').text().replace(/\n\s{1,}|\t/g,?"");//正则去除回车换行等操作符
??let?imgUrl?=?$(item).find('img').attr('src');
??arr.push({
???title,score,imgUrl
??})
?});
?console.log(arr)
});
//输出:
[
??{
????title:?'心灵奇旅',
????score:?'9.1',
????imgUrl:?'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2626308994.jpg'
??},
??{
????title:?'拆弹专家2',
????score:?'8.0',
????imgUrl:?'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2621379901.jpg'
??},
??{
????title:?'晴雅集',
????score:?'5.1',
????imgUrl:?'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2628441592.jpg'
??},
??{
????title:?'神奇女侠1984',
????score:?'6.5',
????imgUrl:?'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2626959989.jpg'
??},
??{
????title:?'明天你是否依然爱我',
????score:?'4.1',
????imgUrl:?'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2628574640.jpg'
??},
??{
????title:?'紧急救援',
????score:?'6.3',
????imgUrl:?'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2627025706.jpg'
??},
??{
????title:?'沐浴之王',
????score:?'6.5',
????imgUrl:?'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2627788612.jpg'
??},
??{
????title:?'棒!少年',
????score:?'8.7',
????imgUrl:?'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2627027305.jpg'
??},
??{
????title:?'疯狂原始人2',
????score:?'8.1',
????imgUrl:?'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2624607255.jpg'
??},
??{
????title:?'赤狐书生',
????score:?'5.1',
????imgUrl:?'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2627575957.jpg'
??},
??{
????title:?'如果声音不记得',
????score:?'4.0',
????imgUrl:?'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2627432193.jpg'
??},
??{
????title:?'隐形人',
????score:?'7.2',
????imgUrl:?'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2624945594.jpg'
??},
??{
????title:?'除暴',
????score:?'6.5',
????imgUrl:?'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2625777429.jpg'
??},
??{
????title:?'猎杀T34',
????score:?'7.5',
????imgUrl:?'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2627487532.jpg'
??},
??{
????title:?'哆啦A梦:大雄的新恐龙',
????score:?'7.5',
????imgUrl:?'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2624375776.jpg'
??},
??{
????title:?'奇妙王国之魔法奇缘',
????score:?'',
????imgUrl:?'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2614907771.jpg'
??},
??{
????title:?'封口者',
????score:?'',
????imgUrl:?'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2619284111.jpg'
??},
??{
????title:?'我和我的家乡',
????score:?'7.4',
????imgUrl:?'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2620453443.jpg'
??},
??{
????title:?'末日逃生',
????score:?'5.6',
????imgUrl:?'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2626067725.jpg'
??},
??{
????title:?'金刚川',
????score:?'6.5',
????imgUrl:?'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2623301908.jpg'
??},
??{
????title:?'真假美猴王之大圣无双',
????score:?'4.1',
????imgUrl:?'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2626695036.jpg'
??},
??{
????title:?'宝可梦:超梦的逆袭?进化',
????score:?'6.4',
????imgUrl:?'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2625913008.jpg'
??},
??{
????title:?'野性的呼唤',
????score:?'7.2',
????imgUrl:?'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2624308041.jpg'
??},
??{
????title:?'汪汪队立大功之超能救援',
????score:?'6.1',
????imgUrl:?'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2624458010.jpg'
??},
??{
????title:?'掬水月在手',
????score:?'8.0',
????imgUrl:?'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2623807993.jpg'
??}
]
5.更进一步,将图片保存至本地
//操作前,先在项目下新建个image目录,用来存放下载的图片
const?superagent?=?require('superagent');
const?cheerio?=?require('cheerio');
var?fs?=?require('fs');//nodejs核心模块,用来处理数据流
let?BASE_URL?=?"https://movie.douban.com/cinema/nowplaying/shijiazhuang/";
superagent.get(BASE_URL)
?.end((err,?res)?=>?{
??if?(err)?{
???return?console.error(err);
??}
??let?$?=?cheerio.load(res.text);
??let?arr?=?[];
??//选取所需节点,并循环
??$('#nowplaying?.lists?.list-item').each((index,?item)?=>?{
???let?title?=?$(item).find('.stitle?.ticket-btn').attr('title');
???let?score?=?$(item).find('.subject-rate').text().replace(/\n\s{1,}|\t/g,?"");
???let?imgUrl?=?$(item).find('img').attr('src');
???//保存图片
???saveImg(imgUrl,title);
???arr.push({
????title,
????score,
????imgUrl
???})
??});
??//?console.log(arr)
?});
//读取并保存图片至本地
function?saveImg(url,name)?{
?superagent.get(url).end(function(err,?sres)?{
??if?(err)?{
???console.log(err);
???return;
??}
??//通过fs将文件写入./image目录,以标题为图片名称
??fs.writeFile(`./image/${name}.jpg`,?sres.body,?"binary",?function(err)?{
???if?(err)?throw?err;
???console.log(`${name}图片保存完成`)
??});
?});
}
node-crawler (轻量级node爬虫工具)
node-crawler,完全由nodejs写成,天生支持非阻塞异步IO。
框架特点
- DOM 元素快速解析 & 符合jQuery语法的选择器功能(默认使用Cheerio,支持更换为 JSDOM 等其它DOM解析器)
- 支持连接池模式,并发数和重连数均可配置
- 支持请求队列的优先权(即不同URL的请求能有不同的优先级)
- 支持延时功能(某些服务器对每分钟内连接数有限制)
- 支持 forceUTF8 模式以应对复杂的编码问题,当然你也可以自己为不同的连接设置编码
- 支持4.x及更高版本的Nodejs
1. 安装
npm install crawler
2. 可控制并发数量、启用慢速模式
var?Crawler?=?require("crawler");
var?c?=?new?Crawler({
????//?最大并发数默认为10
????maxConnections?:?1,
????//?两次请求之间将闲置1000ms
????rateLimit?:?1,
????callback?:?function?(error,?res,?done)?{
????????if(error){
????????????console.log(error);
????????}else{
????????????var?$?=?res.$;
????????????console.log($("title").text());
????????}
????????done();
????}
});
c.queue('http://www.amazon.com');
用新框架实现爬取并下载图片,相关代码如下
var?Crawler?=?require("crawler");
var?fs?=?require('fs');?//nodejs核心模块,用来处理数据流
var?c?=?new?Crawler({
?encoding:?null,
?//?`maxConnections`?将被强制修改为?1
?//?maxConnections?:?10,
?jQuery:?false,
?//?两次请求之间将闲置1000ms
?//?rateLimit:?1000,
?//?在每个请求处理完毕后将调用此回调函数
?callback:?function(error,?res,?done)?{
??if?(error)?{
???console.log(error);
??}?else?{
??????//将图片存放至image文件夹下
???fs.createWriteStream(`./image/${res.options.filename}`).write(res.body);
???console.log(`${res.options.filename}图片保存完成`)
??}
??done();
?}
});
let?arr?=?[];
//去爬取
c.queue([{
?uri:?'https://movie.douban.com/cinema/nowplaying/shijiazhuang/',
?jQuery:?true,
?//?实例化时的callback不会被触发
?callback:?function(error,?res,?done)?{
??if?(error)?{
???console.log(error);
??}?else?{
???var?$?=?res.$;
???//循环获取所需内容
???$('#nowplaying?.lists?.list-item').each((index,?item)?=>?{
????let?title?=?$(item).find('.stitle?.ticket-btn').attr('title');
????let?score?=?$(item).find('.subject-rate').text().replace(/\n\s{1,}|\t/g,?"");
????let?imgUrl?=?$(item).find('img').attr('src');
????//请求图片,请求后会触发实例化时定义的回调函数
????c.queue({
?????uri:?imgUrl,
?????filename:?`${title}.png`
????});
????arr.push({
?????title,
?????score,
?????imgUrl
????})
???});
??}
??done();
?}
}]);
最后:
node实现爬虫基本介绍完毕,大家可以自己着手来开发一个自己的爬虫。
本文暂时没有评论,来添加一个吧(●'◡'●)