Nodejs 爬虫下载图片的问题
Nodejs 爬虫下载图片的问题
写一个爬虫,当只下载比较少的图片时候,一切正常,当下载比较多的图片的时候,会报错。我这里试了两种方式,都会出问题。 1.采用child_process,调用系统的curl下载图片。 代码: var curl=cp.spawn(‘curl’,[file_url]); curl.stdout.on(‘data’,function(data){ file.write(data); }); curl.stdout.on(‘end’,function(data){ file.end(); console.log(filename+‘download to’+DOWNLOAD_DIR); }); 这里省去了on(‘error’)和on(‘exit’) 。最终只能下载几个图片,很多都是失败的,然后报错。 这个报的错误是这样: Error: spawn EMFILE at errnoException (child_process.js:988:11) at ChildProcess.spawn (child_process.js:935:11) at Object.exports.spawn (child_process.js:723:9)
2.所以又采用第二种方式,用http来get这些图片。 var filename=url.parse(file_url).pathname.split(’/’).pop(); var file=fs.createWriteStream(DOWNLOAD_DIR+filename); http.get(file_url,function(res){ res.on(‘data’,function(data){ file.write(data); }); res.on(‘end’,function(data){ file.end(); }); res.on(‘error’,function(e){ if(e){ console.log(‘Errored:’+e); file.end(); }else{ console.log(‘Finish’); } }); }); 这样的效果更不好,也报错了,报的错误是: read econnreset 请教各位,当下载很多图片的时候,要怎么处理呢?
写一个爬虫,当只下载比较少的图片时候,一切正常,当下载比较多的图片的时候,会报错。我这里试了两种方式,都会出问题。 1.采用child_process,调用系统的curl下载图片。 代码: var curl=cp.spawn(‘curl’,[file_url]); curl.stdout.on(‘data’,function(data){ file.write(data); }); curl.stdout.on(‘end’,function(data){ file.end(); console.log(filename+‘download to’+DOWNLOAD_DIR); }); 这里省去了on(‘error’)和on(‘exit’) 。最终只能下载几个图片,很多都是失败的,然后报错。 这个报的错误是这样: Error: spawn EMFILE at errnoException (child_process.js:988:11) at ChildProcess.spawn (child_process.js:935:11) at Object.exports.spawn (child_process.js:723:9)
2.所以又采用第二种方式,用http来get这些图片。 var filename=url.parse(file_url).pathname.split(’/’).pop(); var file=fs.createWriteStream(DOWNLOAD_DIR+filename); http.get(file_url,function(res){ res.on(‘data’,function(data){ file.write(data); }); res.on(‘end’,function(data){ file.end(); }); res.on(‘error’,function(e){ if(e){ console.log(‘Errored:’+e); file.end(); }else{ console.log(‘Finish’); } }); }); 这样的效果更不好,也报错了,报的错误是: read econnreset 请教各位,当下载很多图片的时候,要怎么处理呢?
限制并发用 async.parallelLimit https://github.com/caolan/async#parallellimittasks-limit-callback
不知道Promise有没有 parallelLimit
当你使用 Node.js 编写爬虫并下载大量图片时,可能会遇到一些常见的问题,比如 EMFILE
错误(文件描述符不足)和 ECONNRESET
错误(连接被重置)。这些问题通常是由于资源限制或网络不稳定导致的。以下是一些解决方案和优化方法:
解决方案
-
使用异步库:使用如
axios
或node-fetch
来管理 HTTP 请求,它们通常比原生的http
模块有更好的错误处理机制。 -
限制并发请求:通过使用像
async
库中的queue
功能或p-limit
这样的库来限制同时进行的请求数量,以避免一次性打开太多文件描述符。 -
错误处理和重试机制:实现错误处理和重试逻辑,以便在网络不稳定时可以自动重试。
示例代码
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
// 使用 promisify 将 fs.writeFile 转换为 Promise
const writeFile = promisify(fs.writeFile);
async function downloadImage(url, directory) {
try {
const response = await axios({
url,
responseType: 'stream'
});
const filename = path.basename(new URL(url).pathname);
const filePath = path.join(directory, filename);
// 创建可写流
const writer = fs.createWriteStream(filePath);
// 管道响应数据到文件
response.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});
} catch (error) {
console.error(`Error downloading ${url}:`, error.message);
throw error;
}
}
async function downloadImages(urls, directory) {
for (const url of urls) {
await downloadImage(url, directory);
}
}
// 示例:下载多个图片
const urls = [
'https://example.com/image1.jpg',
'https://example.com/image2.jpg',
// 添加更多 URL
];
const DOWNLOAD_DIR = './downloads';
if (!fs.existsSync(DOWNLOAD_DIR)) {
fs.mkdirSync(DOWNLOAD_DIR);
}
downloadImages(urls, DOWNLOAD_DIR)
.then(() => console.log('All images downloaded successfully'))
.catch(error => console.error('Failed to download images:', error));
解释
-
使用
axios
:axios
是一个流行的 HTTP 客户端,它支持 promise 并且有更好的错误处理能力。 -
创建可写流:使用
fs.createWriteStream
创建一个可写流,并将 HTTP 响应的数据管道到该流中。 -
错误处理和重试:在
downloadImage
函数中捕获错误,并在主函数downloadImages
中处理错误,确保所有错误都被记录。 -
目录检查:在下载之前检查目标目录是否存在,如果不存在则创建目录。
通过这种方式,你可以有效地管理和下载大量图片,同时避免常见的错误。
[@magicdawn](/user/magicdawn) 的确应该是这个并发的原因 我代码里面是没一点限制的
当你需要下载大量图片时,使用 child_process
和 http.get
都可能遇到资源限制或并发问题。一种更好的方法是使用一个专门设计用于处理 HTTP 请求的库,比如 axios
或者 node-fetch
,并且结合 async/await
来更好地管理异步操作。
以下是使用 axios
下载大量图片的一个简单示例:
示例代码
首先,你需要安装 axios
库:
npm install axios
然后你可以编写如下的代码来下载图片:
const fs = require('fs');
const axios = require('axios');
async function downloadImages(urls, downloadDir) {
const downloadPromises = urls.map(async (url) => {
try {
const response = await axios({
url,
responseType: 'stream'
});
const filename = url.split('/').pop();
const filePath = `${downloadDir}/${filename}`;
return new Promise((resolve, reject) => {
response.data.pipe(fs.createWriteStream(filePath))
.on('finish', () => resolve(`Successfully downloaded ${filename}`))
.on('error', (err) => reject(err));
});
} catch (err) {
console.error(`Failed to download ${url}:`, err.message);
}
});
try {
await Promise.all(downloadPromises);
console.log('All images have been successfully downloaded.');
} catch (err) {
console.error('One or more images failed to download:', err);
}
}
// Example usage:
const urls = [
'https://example.com/image1.jpg',
'https://example.com/image2.jpg',
// Add more URLs here
];
const DOWNLOAD_DIR = './downloads';
downloadImages(urls, DOWNLOAD_DIR);
解释
- 安装库:使用
axios
库来进行 HTTP 请求,它比原生的http
模块更容易使用且功能更强大。 - 异步函数:使用
async/await
来处理异步操作,使代码更易读。 - Promise 管理:将每个图片的下载封装在一个
Promise
中,并使用Promise.all
等待所有下载完成。 - 并发控制:通过合理设置并发请求的数量(例如使用
p-limit
库),可以避免一次性发起过多请求导致的资源耗尽问题。
这种方法可以有效地解决下载大量图片时出现的问题。如果你仍然遇到问题,建议检查网络连接、服务器限制以及本地磁盘空间等。