Puppeteer抓取下载直链后如何在Node.js中通过fs模块下载文件
实现方案
你现有提取直链的逻辑不需要改动,只需要调整两部分配置和增加下载落盘逻辑即可:
- 将Puppeteer启动参数的
headless配置改为无头模式,全程不弹出浏览器窗口,提取到直链和必要的请求校验信息后即可直接关闭浏览器实例,不需要保持浏览器运行。 - 拿到文件直链后,不通过
page.click()触发浏览器下载,直接在Node.js侧发起HTTP请求拉取文件流,携带从页面上下文拿到的UA、Cookie、Referer信息绕过站点防盗链校验,最后通过fs模块将流写入自定义路径即可。
这种方案比监听Puppeteer下载事件的方式更灵活,不需要配置浏览器默认下载目录,也不需要额外做下载后的文件移动操作,大文件下载时用流写入也不会占用过高内存。
完整可运行代码
const fs = require('fs'); const https = require('https'); const http = require('http'); const path = require('path'); const puppeteer = require('puppeteer'); (async()=>{ // 无头模式启动,无浏览器窗口 const browser = await puppeteer.launch({ headless: 'new', defaultViewport: false }); const page = await browser.newPage(); const url = `https://speed.hetzner.de/`; await page.goto(url, {"waitUntil": "networkidle2"}); // 原有提取直链逻辑保持不变 const downloadUrl = await page.$eval('p:nth-child(2) > a', elm => elm.href); console.log('提取到的文件直链:', downloadUrl); // 自定义存储路径,示例为当前目录下的downloads文件夹,可自行修改 const saveDir = path.resolve(__dirname, './downloads'); if (!fs.existsSync(saveDir)) { fs.mkdirSync(saveDir, { recursive: true }); } // 自动从url解析文件名,也可自定义文件名 const fileName = path.basename(new URL(downloadUrl).pathname); const savePath = path.join(saveDir, fileName); // 拿取页面上下文的请求头信息,绕过防盗链 const userAgent = await page.evaluate(() => navigator.userAgent); const cookies = await page.cookies(downloadUrl); const cookieStr = cookies.map(item => `${item.name}=${item.value}`).join('; '); // 拿到必要信息后直接关闭浏览器,后续下载不需要浏览器参与 await browser.close(); // 流方式下载写入本地,支持大文件 const requestClient = downloadUrl.startsWith('https') ? https : http; const writeStream = fs.createWriteStream(savePath); const sendRequest = (targetUrl) => { requestClient.get(targetUrl, { headers: { 'User-Agent': userAgent, 'Cookie': cookieStr, 'Referer': url } }, (res) => { // 自动处理3xx重定向 if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { sendRequest(res.headers.location); return; } if (res.statusCode !== 200) { writeStream.destroy(); fs.unlinkSync(savePath); console.error('下载失败,状态码:', res.statusCode); return; } res.pipe(writeStream); }).on('error', (err) => { writeStream.destroy(); fs.existsSync(savePath) && fs.unlinkSync(savePath); console.error('请求失败:', err); }); }; sendRequest(downloadUrl); writeStream.on('finish', () => { writeStream.close(); console.log(`文件下载完成,存储路径: ${savePath}`); }); writeStream.on('error', (err) => { fs.existsSync(savePath) && fs.unlinkSync(savePath); console.error('文件写入失败:', err); }); })();
注意事项
- 如果目标站点没有防盗链校验,可以省略拿Cookie、UA、Referer的步骤,直接请求直链即可。
- 不想用原生http/https模块的话,也可以替换成
axios等第三方请求库,记得将responseType设置为stream即可,落盘逻辑和上面一致。 - 代码里默认自动创建存储目录、自动解析文件名,你可以根据业务需求修改为任意自定义路径和文件名。
内容的提问来源于stack exchange,提问作者New coder
相关产品推荐
相关产品推荐

