上次的網頁爬蟲寫完后,又打算做一個爬圖的工具。前兩天已經寫好了代碼。思路如下:
分析頁面還是采用cheerio,對<div>中的img進行分析抽取,拿到圖片的url。然后用childprocess調用系統的curl工具來下載這些遠程url的圖片。最后將這些寫入到系統的硬盤中。
功能代碼如下(只是下載圖片的功能代碼):
var url=require('url'); var fs=require('fs'); var cp=require('child_process'); var DOWNLOAD_DIR='./'; var file_url='http://htmljs.b0.upaiyun.com/uploads/1396874069658-nodejs_1280x1024.png'; function downloads(file_url){ var filename=url.parse(file_url).pathname.split('/').pop(); var file=fs.createWriteStream(DOWNLOAD_DIR+filename); var curl=cp.spawn('curl',[file_url]); //use spawn curl.stdout.on('data',function(data){ file.write(data); }); curl.stdout.on('end',function(data){ file.end(); console.log(filename+'downloaded to'+DOWNLOAD_DIR); }); curl.on('exit',function(code){ if(code!=0){ console.log('Failed:'+code); } }); }; downloads(file_url);
但是發現了一個問題。就是下載的圖的數量比較少的時候,一切都還好。當循環頁面下載的時候,一旦並行任務過多,立刻就不行了。因為並發量太大。
后來嘗試用http的get方法來讀圖片,結果性能更不行,還不如系統的curl。
去cnode請教了一下,有前輩說需要限制一下並發量,用aysnc。
也是在下班后等零碎的時間,去aysnc的github(https://github.com/caolan/async)上看了一下,運行了幾個實例。發現async的確不錯。
先說下js異步回調的問題,按照傳統的回調來,處理回調中的數據,發現只能一層套一層的。剛開始感覺這種方式不錯,后來套的多了,才發現這種方式,太死了。
async是解決這個問題的。
為了解決我遇到的問題,所以着重看了一下它的控制流方面的東西。
async.series 和parallel.
這兩個,一個是順序的一個是並行的。
api里面有講他的用法,他們都有(tasks, [callback])的參數。第一個參數是任務,第二個是得到前面任務數據的callback。
其中多個task,可以用數組,也可以用對象。本人傾向用對象。
async.series({ one: function(callback){ callback(null, 'one'); }, two: function(callback){ callback(null, 'two'); } }, function(err, results) { // results is now equal to: {one: 'one', two: 'two'} });
同樣,可以打印results中的某些屬性,results.one什么的。
parallel和series是一樣的用法。
所以,采用這種並行的方式,把之前寫的工具改造一下,我們就不需要再往下套函數處理了:
var cheerio=require('cheerio'); var request=require('request'); var fs=require('fs'); var async=require('async'); var url='http://www.cnblogs.com/juepei/'; var result=new Array(); async.parallel({ one:function(cb){ request(url,function(error,res,body){ if(!error && res.statusCode==200){ var $=cheerio.load(body); $('div').filter(function(i,ele){ if($(this).attr('class')==='postTitle'){ result.push($(this).text().trim()); } }); //console.log(result); cb(result); } }); } },function(err,results){ if(err){ console.log(err); }else{ console.log(results.one); } });
關於最后一個回調函數,必須要處理err,不然的話,會報results為undefined。這是不是async規定的,目前還不知道,如果有前輩知道為何,請賜教。
從而,再下載大量圖片的時候,我們就可以采用parallelLimit(tasks, limit, [callback])這個函數,對並行任務的數量進行限制一下,就可解決問題了。晚上回去試試,看是不是已經ok了。
在此附上本人的github地址:https://github.com/ThinkCats ,大家可以相互交流,共同進步。