用Nodejs爬文件遇到的问题,请各位大大来看看
用Nodejs爬文件遇到的问题,请各位大大来看看
我想用nodejs 的http.get爬一堆文件下来,但是这个网站做了限制每次只能同时下载2个文件,而nodejs是异步的,问题来了…例如:
var urlArr = [ /* 一堆文件的url */ ];
for(/* 遍历urlArr */ ) {
http.get(url, ...); // http.get 下载文件
}
怎样做到每次只爬2个文件,或者第一次爬2个文件,然后这两个文件某个爬完了,又自动去爬下一个,总之每次只能有2个下载的请求,因为多了的那些请求是没法下载文件。。
期待大家的解答~~ 灰常感谢~~
当然可以。针对你的需求,我们可以使用 async
和 await
来控制并发请求的数量。下面是一个简单的示例代码,展示了如何实现每次只下载两个文件,并在下载完成后继续下载下一个文件。
示例代码
const https = require('https');
const fs = require('fs');
const path = require('path');
// 文件URL数组
const urlArr = [
'https://example.com/file1.zip',
'https://example.com/file2.zip',
'https://example.com/file3.zip',
// 添加更多文件URL
];
// 创建一个队列来管理请求
const downloadQueue = [];
let activeDownloads = 0;
// 控制并发数量的函数
async function downloadFile(url) {
return new Promise((resolve, reject) => {
const filename = path.basename(new URL(url).pathname);
const file = fs.createWriteStream(filename);
https.get(url, (response) => {
response.pipe(file);
file.on('finish', () => {
file.close(() => resolve(`Downloaded ${filename}`));
});
}).on('error', (err) => {
file.close();
reject(err.message);
});
});
}
// 检查并启动新的下载任务
function startNextDownload() {
if (downloadQueue.length > 0 && activeDownloads < 2) {
const url = downloadQueue.shift();
activeDownloads++;
downloadFile(url)
.then(() => {
console.log(`Completed: ${url}`);
activeDownloads--;
startNextDownload(); // 启动下一个下载任务
})
.catch((err) => {
console.error(`Error downloading ${url}: ${err}`);
activeDownloads--;
startNextDownload(); // 启动下一个下载任务
});
}
}
// 将所有URL添加到队列中
urlArr.forEach((url) => {
downloadQueue.push(url);
});
// 启动初始的两个下载任务
startNextDownload();
解释
- 创建下载队列:我们首先将所有需要下载的文件URL放入一个队列中。
- 控制并发数量:我们定义了一个
activeDownloads
变量来跟踪当前正在执行的下载任务数量。每次开始一个新的下载任务时,该变量会增加;当下载完成时,该变量会减少。 - 下载文件:我们使用
https.get
方法来下载文件,并将其保存到本地。downloadFile
函数返回一个Promise,用于处理异步操作。 - 启动下载任务:
startNextDownload
函数负责检查队列并启动新的下载任务,确保每次只有两个下载任务同时进行。 - 初始化:我们将所有URL添加到队列中,并调用
startNextDownload
来启动初始的两个下载任务。
这样,我们就能够有效地控制并发下载的数量,并且在每次下载完成后自动启动下一个下载任务。希望这对你有所帮助!
用老赵的 jscex
正在尝试,但是不知道如何实现,求请教~
var urlArr = […]; count =0; for(…){ while(count==2){ // 休息10分钟 } // 在callback 里面 count-=1; http.get(url,…); count +=1; }
不知道可以不?
文件有大有小,时间不好设置,太长了浪费时间,太短了,那就没法控制2个请求
哈哈,async 这个不是很熟悉,我自己用EventEmitter搞定了~~
/**
* 下载文件
*
* [@param](/user/param) {array} urlArr 一堆文件的url
* [@param](/user/param) {int} threadCount 线程总数
* [@param](/user/param) {function} callback 下载完成后的回调函数
*/
var download = function (urlArr, callback) {
// 模拟的线程
var thread = function () {
var url = urlArr.shift(); // 从URL列表中取一个出来
if (typeof url !== 'string') {
return threadDone();
}
http.get(url, function () { // http.get 下载文件
// 处理下载回来的文件
// ...
thread();
});
};
var finishThread = 0; // 已完成的线程数
var threadDone = function () {
finishThread++;
if (finishThread >= threadCount) {
return callback();
}
};
for (var i = 0; i < threadCount; i++) {
thread();
}
};
使用方法:
download(['url1', 'url2', 'url3'], 2, function () {
// 下载完成了...
});
综合起来貌似用async的forEachLimit最简单~~
我觉得用node.io最简单, 内建一个参数就支持几个线程下载
就跟很多人说了用async.forEachLimit
就行了,forEachLimit
里面用Jscex写逻辑。如果你真要用Jscex——虽然我不觉得Jscex是处理这些事情的,就在循环里用whenAny
吧,whenAny
就是等待任意一个完成。还有真的,看看文档吧,不要搞到最后文档是白写的……
不用EventProxy, 用Bagpipe马到功成。 https://github.com/JacksonTian/bagpipe
var urlArr = [ /* 一堆文件的url */ ];
var Bagpipe = require('bagpipe');
var bagpipe = new Bagpipe(2);
for(/* 遍历urlArr */ ) {
// http.get(url, ...); // http.get 下载文件
bagpipe.push(http.get, url, ..., callback);
}
代码基本不用怎么改
太复杂 有没有。
太复杂 有没有。
要实现每次只同时处理两个文件下载请求,可以使用 async
和 await
结合 Promise
来管理并发数量。这里提供一个简单的示例代码来说明如何实现这个功能:
const http = require('http');
const fs = require('fs');
const urlArr = [/* 你的文件URL数组 */];
async function downloadFile(url) {
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(`./${Date.now()}.tmp`);
http.get(url, response => {
response.pipe(file);
file.on('finish', () => {
file.close(resolve());
});
}).on('error', err => {
fs.unlink(file.path, () => { /* 文件已下载完毕,但发生了错误,删除已下载的部分文件 */ });
reject(err.message);
});
});
}
async function handleDownloads(urls, limit = 2) {
let downloading = []; // 用于存储正在下载的Promise
async function processQueue(queue) {
while (queue.length > 0) {
const url = queue.shift();
downloading.push(downloadFile(url));
if (downloading.length >= limit) {
const done = await Promise.race(downloading);
downloading = downloading.filter(promise => promise !== done);
}
}
await Promise.all(downloading);
}
await processQueue(urls);
}
handleDownloads(urlArr);
这段代码首先定义了一个 downloadFile
函数,它返回一个 Promise
,表示文件下载完成或失败。接着定义了一个 handleDownloads
函数,该函数使用一个队列来管理待处理的 URL,并确保同时最多只有 limit
个文件在下载。
当你调用 handleDownloads(urlArr)
时,它将开始下载文件,每次最多同时下载两个。当一个下载完成后,下一个文件会自动加入到下载队列中。
这种模式能够有效地管理并发请求,避免超过服务器规定的最大连接数限制。