在上一篇中《Node.js爬取妹子图-crawler爬虫的使用》,主要介绍的crawler。之前在写《Node.js 2小时爬取驴妈妈8W+条旅游数据》时,使用的是node-schedule,做的定时任务,这样就可以不间断的爬取了。
这次在爬取妹子图使用的是async,这次的效率明显的提升了很多,感觉1分钟就爬取了37581条数据,再来8W条也是小case。
。
在上一篇《Node.js爬取妹子图crawler爬虫的使用》中的function downloadContent(i,c)
方法,我卖了一个关子,这里就做说明,顺便介绍一下async。
关于async这个,大家还是先看文档,https://caolan.github.io/async/。如果你在使用一个新东西之前,连看文档的心都没有,我不建议你使用了。
其实在《Node.js 批量下载图片》中,我就使用过async
,只是当时没说。上次使用的是each
,这次使用的是队列queue
。
妹子图中,一共有72页数据,2806个主题,37581张图片。
1.爬取2806个主题
// 浏览器访问一下之后就断掉,不要继续链接这个地址
router.get('/api/crawler/crawler', function(req, resq, next){
var c = new Crawler({
maxConnections : 10,
retries: 5,
// This will be called for each crawled page
callback : function (error, res, done) {
if(error){
console.log(error);
}else{
var $ = res.$;
console.log($("title").text(),'1');
}
done();
}
});
c.queue([{
uri: 'http://www.meizitu.com/a/more_1.html',
jQuery: true,
callback: function (error, res, done) {
if(error){
console.log(error);
}else{
var $ = res.$;
var total_pag = 0;
$('#wp_page_numbers li a').each(function(index,item){
if ($(item).text() == '末页') {
total_pag = $(item).attr('href');
var regexp = /[0-9]+/g;
total_pag = total_pag.match(regexp)[0]; // 总页数
}
})
var tempArray = [];
var q = async.queue(function(task, callback) {
console.log('hello ' + task.name);
downloadContent(task.name,c); // 每个人物都会执行一个这个
callback();
}, total_pag);
q.drain = function() {
console.log('all items have been processed');
};
for (var i = 1; i <= total_pag; i ++) {
var item = {
name: i // 队列中的每个人物都有个 name
}
tempArray.push(item);
}
// 添加进队列
q.push(tempArray, function(err) {
console.log('finished processing item');
});
console.log(total_pag); // 72
}
done();
}
}]);
})
// 分页下载
function downloadContent(i,c){
var uri = 'http://www.meizitu.com/a/more_' + i + '.html';
c.queue([{
uri: uri,
jQuery: true,
callback: function (error, res, done) {
if(error){
console.log(error);
}else{
var $ = res.$;
var meiziSql = '';
$('.wp-item .pic a').each(function(index,item){
var href = $(item).attr('href'); // 获取路径uri
var regexp = /[0-9]+/g;
var artice_id = href.match(regexp)[0]; // 获取文章ID
var title = $(item).children('img').attr('alt');
title = title.replace(/<[^>]+>/g,""); // 去掉 <b></b> 标签
var src = $(item).children('img').attr('src');
var create_time = new Date().getTime();
if (href == 'http://www.meizitu.com/a/3900.html') {
title = title.replace(/'/g,''); // 这个的标题多了一个 单引号, mysql在插入的时候存在问题,所以这样处理了一下
}
var values = "'" + artice_id + "'" + ','
+ "'" + title + "'" + ','
+ "'" + href + "',"
+ "'" + src + "'" + ','
+ "'" + create_time + "'";
meiziSql = meiziSql + 'insert ignore into meizitu_all(artice_id,title,href,src,create_time) VALUES(' + values + ');';
})
pool.getConnection(function(err, connection) {
if(err){
console.log('数据库连接失败',i);
}
connection.query(meiziSql,function (err, results) {
connection.release();
if (err){
console.log(err,i);
}else{
console.log('插入成功',i);
}
})
})
}
done();
}
}]);
}
在写到这里的时候,突然想到了之前玩stm32
的时候,跑ucos ii
的时候,也有这个概念也有这个词task
。
首先获取页数,然后每页的爬取。
2.爬取37581张图片
router.get('/api/crawler/crawler2', function(req, resq, next){
pool.getConnection(function(err, connection) {
if(err){
console.log('数据库连接失败');
}
connection.query('select * from meizitu_all',function (err, results) {
connection.release();
if (err){
console.log('数据库查询失败');
}else{
var artices = results;
var c = new Crawler({
maxConnections : 10,
retries: 5,
// This will be called for each crawled page
callback : function (error, res, done) {
if(error){
console.log(error);
}else{
var $ = res.$;
console.log($("title").text(),'1');
}
done();
}
});
var total_pag = artices.length;
var tempArray = [];
var q = async.queue(function(task, callback) {
console.log('hello ' + task.name);
download2(task.name,task.artice_id,c,task.index);
callback();
}, total_pag);
q.drain = function() {
console.log('all items have been processed');
};
for (var i = 0; i < total_pag; i ++) {
var href = artices[i].href;
var artice_id = artices[i].artice_id;
var item = {
name: href, // 队列中,每个任务要有一个 name 其他字段可以自行添加
artice_id: artice_id,
index: i
}
tempArray.push(item);
}
q.push(tempArray, function(err) {
console.log('finished processing item');
});
console.log(total_pag);
}
})
})
})
// 下载详情
function download2(name,artice_id,c,index){
c.queue([{
uri: name,
jQuery: true,
callback: function (error, res, done) {
if(error){
console.log(error);
}else{
var $ = res.$;
var meiziSql = '';
if ($('#picture p img').length != 0) {
$('#picture p img').each(function(index,item){
var pic_alt = $(item).attr('alt');
var pic_src = $(item).attr('src');
var create_time = new Date().getTime();
var values = "'" + artice_id + "'" + ','
+ "'" + pic_alt + "'" + ','
+ "'" + pic_src + "',"
+ "'" + create_time + "'";
meiziSql = meiziSql + 'insert ignore into meizitu_detail(artice_id,pic_alt,pic_src,create_time) VALUES(' + values + ');';
})
}
if ($('.postContent img').length != 0) {
$('.postContent img').each(function(index,item){
var pic_alt = $(item).attr('alt');
var pic_src = $(item).attr('src');
var create_time = new Date().getTime();
var values = "'" + artice_id + "'" + ','
+ "'" + pic_alt + "'" + ','
+ "'" + pic_src + "',"
+ "'" + create_time + "'";
meiziSql = meiziSql + 'insert ignore into meizitu_detail(artice_id,pic_alt,pic_src,create_time) VALUES(' + values + ');';
})
}
if (meiziSql != '') {
pool.getConnection(function(err, connection) {
if(err){
console.log('数据库连接失败',index);
}
connection.query(meiziSql,function (err, results) {
connection.release();
if (err){
console.log(err,index,artice_id);
}else{
console.log('插入成功',index,artice_id);
}
})
})
}
}
done();
}
}]);
}
先从数据库中取出,上一步存储的主题,然后加入队列任务爬取和存储。
这次感觉使用的队列之后,爬取效率大大的提升。