Nodejs大批量下載圖片入坑指南(使用async和bagpipe處理大并發(fā)量請求)

前言

故事還得從頭說起吼砂。烏云網(wǎng)掛掉之后,烏云知識庫也無法訪問了箭阶。曾經(jīng),在上面看到那么多優(yōu)秀的安全類文章,一下子看不到了,頗覺得有點不適應晤柄。還好網(wǎng)上流傳著民間的各種版本,于是我收集了一下,放在了Github上歪架。這些文章只是一些html文件,并不包含頁面上的圖片吉捶。幸運的是,圖片的域名static.wooyun.com還可以繼續(xù)訪問,因此有必要把這些圖片也抓取下來我碟。

Wooyun Drops 文章在線瀏覽

Wooyun Drops 文章在線瀏覽
Github: wooyun_articles

使用Nodejs下載圖片

抓取圖片鏈接的過程在此不再詳述,無非就是打開每個html頁面,找到其中img標簽的src屬性放案。我們拿到了這些html頁面的圖片鏈接,是這個樣子的:

var imageLinks = [
    'http://static.wooyun.org//drops/20160315/2016031513484536696130.png',
    'http://static.wooyun.org//drops/20160315/2016031513484767273224.png',
    'http://static.wooyun.org//drops/20160315/2016031513485057874324.png',
    'http://static.wooyun.org//drops/20160315/2016031513485211060411.png',
    'http://static.wooyun.org//drops/20160315/201603151348542560759.png',
    'http://static.wooyun.org//drops/20160315/201603151348563741969.png',
    'http://static.wooyun.org//drops/20160315/201603151348582588879.png',
    'http://static.wooyun.org//drops/20160315/201603151349001461288.png',
    'http://static.wooyun.org//drops/20160315/201603151349032455899.png',
    ......
    'http://static.wooyun.org//drops/20150714/2015071404144944570.png',
    'http://static.wooyun.org//drops/20150714/2015071404144966345.png',
    'http://static.wooyun.org//drops/20150714/2015071404144915704.png',
    'http://static.wooyun.org//drops/20150714/2015071404144980399.png',
    'http://static.wooyun.org//drops/20150714/2015071404144927633.png'
];

我們將其定義為一個模塊備用(總共大約有13000個圖片鏈接)。

在Nodejs中下載圖片有很多解決方案,比如可以使用npm包download矫俺。本文中使用比較簡單的版本,具體實現(xiàn)如下:

var fs = require("fs");
var path = require('path');
var request = require('request');

var downloadImage = function(src, dest, callback) {
    request.head(src, function(err, res, body) {
        // console.log('content-type:', res.headers['content-type']);
        // console.log('content-length:', res.headers['content-length']);
        if (src) {
            request(src).pipe(fs.createWriteStream(dest)).on('close', function() {
                callback(null, dest);
            });
        }
    });
};

代碼來源: stackoverflow吱殉。

比如想要下載一個圖片,可以這樣子來做:

downloadImage("http://static.wooyun.org/20140918/2014091811544377515.png", "./1.png", function(err, data){
    if (err) {
        console.log(err)
    }
    if (data) {
        console.log("done: " + data);
    }
})

對于我們想要的批量下載圖片,最直觀的做法就是寫一個循環(huán),然后去調(diào)用downloadImage方法。但是由于圖片鏈接比較多,總是在下載到一部分圖片后出現(xiàn)一些錯誤:

events.js:160
      throw er; // Unhandled 'error' event
      ^

Error: Invalid URI "undefined"
    at Request.init (/Users//dirread/node_modules/request/request.js:275:31)
    at new Request (/Users/dirread/node_modules/request/request.js:129:8)
    at request (/Users/dirread/node_modules/request/index.js:55:10)
    at Request._callback (/Users//down.js:21:11)
    at self.callback (/Users//dirread/node_modules/request/request.js:187:22)
    at emitOne (events.js:96:13)
    at Request.emit (events.js:188:7)

因此只有考慮使用異步方式來處理厘托。

解決方案一: 使用async異步批量下載圖片

Async是一個流程控制工具包友雳,提供了直接而強大的異步功能。其提供了大約20個函數(shù)铅匹,包括常用的 map, reduce, filter, forEach 等押赊,異步流程控制模式包括,串行(series)包斑,并行(parallel)考杉,瀑布(waterfall)等。

安裝async

npm install async --save

其使用方式也比較簡單,比如下面的示例:

async.map(['file1','file2','file3'], fs.stat, function(err, results){
    // results is now an array of stats for each file
});

async.filter(['file1','file2','file3'], function(filePath, callback) {
  fs.access(filePath, function(err) {
    callback(null, !err)
  });
}, function(err, results){
    // results now equals an array of the existing files
});

async.parallel([
    function(callback){ ... },
    function(callback){ ... }
], function(err, results) {
    // optional callback
};

async.series([
    function(callback){ ... },
    function(callback){ ... }
]);

關(guān)于async的示例在此不再展開,具體可以參考alsotang的demo和教程舰始。

使用async下載圖片

回到我們的程序,我們可以使用來實現(xiàn)崇棠。具體代碼如下:

async.mapSeries(imageLinks, function(item, callback) {
    //console.log(item);
    setTimeout(function() {
        if (item.indexOf("http://static.wooyun.org") === 0) {
            var destImage = path.resolve("./images/", item.split("/")[item.split("/").length -1]);
            downloadImage(item, destImage, function(err, data){
                console.log("["+ index++ +"]: " + data);
            });
            
        }
        callback(null, item);
    }, 100);


}, function(err, results) {});

完整代碼可以參看github上

運行

node down.js

即可看到結(jié)果丸卷。

踩坑提醒

在Mac上使用的時候,偶爾會出現(xiàn)這樣的錯誤:

Error: EMFILE, too many open files

有可能是因為Mac對并發(fā)打開的文件數(shù)限制的比較小,一般為256.所以需要修改一下枕稀。
在命令行執(zhí)行:

echo kern.maxfiles=65536 | sudo tee -a /etc/sysctl.conf
echo kern.maxfilesperproc=65536 | sudo tee -a /etc/sysctl.conf
sudo sysctl -w kern.maxfiles=65536
sudo sysctl -w kern.maxfilesperproc=65536
ulimit -n 65536 65536

然后可以在你的.bashrc文件中加入一行:

ulimit -n 65536 65536

解決方案來自stackoverflow.

解決方案二: 使用bagpipe批量下載圖片

bagpile是樸靈大大做的一個在nodejs中控制并發(fā)執(zhí)行的模塊。

安裝bagpipe

其安裝和使用也比較簡單:

npm install bagpipe --save

使用示例:

var bagpipe = new Bagpipe(10);

var files = ['這里有很多很多文件'];
for (var i = 0; i < files.length; i++) {
  // fs.readFile(files[i], 'utf-8', function (err, data) {
  bagpipe.push(fs.readFile, files[i], 'utf-8', function (err, data) {
    // 不會因為文件描述符過多出錯
    // 妥妥的
  });
}

使用bagpipe批量下載圖片

將我們的代碼稍做修改,就可以使用bagpipe了谜嫉。具體代碼如下:

for (var i = 0; i < imageLinks.length; i++) {
    if (imageLinks[i].indexOf("http://static.wooyun.org") === 0) {
        var destImage = path.resolve("./images/", imageLinks[i].split("/")[imageLinks[i].split("/").length -1]);
        bagpipe.push(downloadImage, imageLinks[i], destImage, function(err, data) {
            console.log("["+ index++ +"]: " + data);
        });
    }
}

完整代碼可以參考github上萎坷。

運行

node down.js

即可看到結(jié)果。

結(jié)語

async和bagpipe都是很優(yōu)秀的nodejs包,本身async功能十分強大,bagpipe使用起來簡單方便,對原有的異步代碼幾乎不必做太多改動沐兰。因此可以根據(jù)自己喜好選擇使用哆档。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市住闯,隨后出現(xiàn)的幾起案子瓜浸,更是在濱河造成了極大的恐慌澳淑,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件插佛,死亡現(xiàn)場離奇詭異杠巡,居然都是意外死亡,警方通過查閱死者的電腦和手機雇寇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門氢拥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锨侯,你說我怎么就攤上這事嫩海。” “怎么了囚痴?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵出革,是天一觀的道長。 經(jīng)常有香客問我渡讼,道長骂束,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任成箫,我火速辦了婚禮展箱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蹬昌。我一直安慰自己混驰,他們只是感情好,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布皂贩。 她就那樣靜靜地躺著栖榨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪明刷。 梳的紋絲不亂的頭發(fā)上婴栽,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天,我揣著相機與錄音辈末,去河邊找鬼愚争。 笑死,一個胖子當著我的面吹牛挤聘,可吹牛的內(nèi)容都是我干的轰枝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼组去,長吁一口氣:“原來是場噩夢啊……” “哼鞍陨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起从隆,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤诚撵,失蹤者是張志新(化名)和其女友劉穎缭裆,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砾脑,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡幼驶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年艾杏,在試婚紗的時候發(fā)現(xiàn)自己被綠了韧衣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡购桑,死狀恐怖畅铭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情勃蜘,我是刑警寧澤硕噩,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站缭贡,受9級特大地震影響炉擅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜阳惹,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一谍失、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧莹汤,春花似錦快鱼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至止潮,卻和暖如春窃判,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喇闸。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工兢孝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仅偎。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓跨蟹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親橘沥。 傳聞我的和親對象是個殘疾皇子窗轩,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,077評論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件座咆、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,094評論 4 62
  • 這么帥氣的人兒大家肯定都認識吧痢艺,沒錯仓洼,他就是鄭凱,隨著第三季跑男的結(jié)束堤舒,他也與跑男一起走過了三個季度色建,他,沒有鄧叔...
    coffee漫閱讀 311評論 2 2
  • subvention(subsidy:補貼a grant, aid, or subsidy, as from a ...
    相關(guān)知情人士閱讀 622評論 0 0
  • 從夜市上買回了衣服和芭比娃娃舌缤。在挑選衣服時箕戳,起初看到了一件全棉的連衣裙,不論是款式国撵、質(zhì)地還是價格都符合內(nèi)心的標準陵吸。...
    云淡風輕66閱讀 426評論 0 0