前言
故事還得從頭說起吼砂。烏云網(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ù)自己喜好選擇使用哆档。