请求优化Node.js+Puppeteer代码:按TXT行串行/限并发执行任务
Node.js Puppeteer 串行/限并发优化方案
原代码的问题在于lineReader.eachLine会并行触发回调,导致同时启动大量浏览器实例,既不符合串行/限并发要求,也容易触发目标网站的反爬机制。以下是针对性的优化方案:
一、基础问题修正
- 修复变量名冲突:将
var Date改为const targetDate,避免覆盖内置Date对象 - 补上缺失的
puppeteer模块引入 - 移除
process.setMaxListeners(Infinity),优化后不再需要此配置
二、串行处理方案
先一次性读取所有账号,再逐个处理每个账号,完成后等待15秒再执行下一个任务:
const puppeteer = require('puppeteer'); const fs = require('fs').promises; const targetDate = '2022-10-12'; // 通用等待函数 const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)); async function processAccount(line) { console.log(`开始处理账号: ${line}`); const url = `https://mining.alienworlds.tools/?account=${line}&date=${targetDate}&skip=0`; try { const browser = await puppeteer.launch({ headless: true, args: ['--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'] }); const page = await browser.newPage(); await page.setViewport({ width: 1920, height: 500 }); await page.goto(url, { waitUntil: 'networkidle2' }); // 等待目标元素加载完成 const element = await page.waitForSelector("#__layout > div > div > div > div.row > div.col-lg-6.col-account > div > div:nth-child(3) > div"); const value = await element.evaluate(el => el.textContent.trim()); console.log(`${line} - ${value}`); await browser.close(); console.log(`账号${line}处理完成,等待15秒后继续`); await wait(15000); } catch (error) { console.error(`处理账号${line}出错:`, error); // 出错后仍等待15秒,避免频繁请求触发反爬 await wait(15000); } } async function main() { try { // 读取所有账号并过滤空行 const content = await fs.readFile('accounts.txt', 'utf8'); const lines = content.split('\n').filter(line => line.trim()); // 串行遍历处理每个账号 for (const line of lines) { await processAccount(line); } console.log('所有账号处理完成'); } catch (error) { console.error('读取账号文件出错:', error); } } main();
三、限制2-3个并发的方案
如果需要同时运行2-3个浏览器实例,可采用批次并行处理的方式:
const puppeteer = require('puppeteer'); const fs = require('fs').promises; const targetDate = '2022-10-12'; const CONCURRENCY_LIMIT = 2; // 可根据需求改为3 const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)); async function processAccount(line) { console.log(`开始处理账号: ${line}`); const url = `https://mining.alienworlds.tools/?account=${line}&date=${targetDate}&skip=0`; try { const browser = await puppeteer.launch({ headless: true, args: ['--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'] }); const page = await browser.newPage(); await page.setViewport({ width: 1920, height: 500 }); await page.goto(url, { waitUntil: 'networkidle2' }); const element = await page.waitForSelector("#__layout > div > div > div > div.row > div.col-lg-6.col-account > div > div:nth-child(3) > div"); const value = await element.evaluate(el => el.textContent.trim()); console.log(`${line} - ${value}`); await browser.close(); console.log(`账号${line}处理完成`); } catch (error) { console.error(`处理账号${line}出错:`, error); } } async function main() { try { const content = await fs.readFile('accounts.txt', 'utf8'); const lines = content.split('\n').filter(line => line.trim()); // 按并发限制分批处理 for (let i = 0; i < lines.length; i += CONCURRENCY_LIMIT) { const batch = lines.slice(i, i + CONCURRENCY_LIMIT); // 并行处理当前批次的账号 await Promise.all(batch.map(line => processAccount(line))); // 非最后一批处理完成后等待15秒 if (i + CONCURRENCY_LIMIT < lines.length) { console.log('当前批次处理完成,等待15秒后继续'); await wait(15000); } } console.log('所有账号处理完成'); } catch (error) { console.error('读取账号文件出错:', error); } } main();
关键改动说明
- 改用
fs.promises.readFile一次性读取所有账号,便于控制执行顺序 - 提取
processAccount函数封装单账号处理逻辑,代码结构更清晰 - 串行方案通过
for...of结合await确保任务逐个执行,每个任务完成后等待15秒 - 限并发方案通过
Promise.all处理批次任务,批次间等待15秒控制请求频率 - 增加全局错误捕获,避免单个账号处理失败导致整个程序崩溃
- 移除原代码中冗余的
waitFor(1000),page.goto的networkidle2已足够确保页面加载完成
内容的提问来源于stack exchange,提问作者Gambit687
相关产品推荐
相关产品推荐

