Node.js中setInterval无报错但未按周期执行爬虫函数问题
问题根因
- 错误引入
timers/promises模块下的setInterval:该API是供异步迭代使用的工具方法,和全局环境下接收回调、按固定间隔执行的setInterval行为完全不同,是定时逻辑失效的核心原因。 - 定时任务传参错误:
setInterval(scrape(), 4000)写法会在代码运行到该行时立刻执行一次scrape(),将其返回的Promise对象作为参数传入,而非将scrape函数本身作为执行回调传入,无法触发周期执行。 - 存在资源泄漏:每次执行
scrape都会启动全新的浏览器实例,爬取完成后未关闭释放进程,运行2次后就会因为系统资源占用过高阻塞后续执行。 - 间隔参数不匹配:需求为5秒间隔,代码中传入的4000毫秒对应4秒间隔,不符合预期。
代码仅打印2次结果的现象和上述问题完全对应:setInterval行内立刻执行1次爬取,后续手动调用scrape()再执行1次,之后因API错误、资源耗尽无后续输出。
修复后可运行代码
const puppeteer = require('puppeteer-extra'); const StealthPlugin = require('puppeteer-extra-plugin-stealth'); const cheerio = require('cheerio'); require('dotenv').config(); const accountSid = process.env.TWILIO_ACCOUNT_SID; const authToken = process.env.TWILIO_AUTH_TOKEN; const client = require('twilio')(accountSid,authToken); puppeteer.use(StealthPlugin()); const oldData = "oldData"; async function scrape(){ let browser; try { browser = await puppeteer.launch({ headless: true, defaultViewport:{ width: 1920, height: 1080 } }); const page = await browser.newPage(); // 替换为实际目标页面地址 await page.goto('page'); const pageData = await page.evaluate(() => { return{ width: document.documentElement.clientWidth, height: document.documentElement.clientHeight, html: document.documentElement.innerHTML, }; }); const $ = cheerio.load(pageData.html); const element = $(".accordion__header-inner:last"); console.log(element.text()); } catch (err) { // 捕获单次爬取异常,避免定时任务整体终止 console.error('爬取执行异常:', err.message); } finally { // 无论爬取成功/失败,都关闭浏览器释放资源 if (browser) await browser.close(); } }; // 首次启动立即执行一次 scrape(); // 传入函数引用,设置5秒间隔 const timerHandle = setInterval(scrape, 5000); // 程序退出前可调用 clearInterval(timerHandle) 清理定时任务
优化建议
- 清理冗余引入:代码中未使用的
fs模块、从cheerio解构的html变量都可以直接删除,减少不必要的加载开销。 - 可复用浏览器实例:如果爬取频率较高,不需要每次执行都重新启动浏览器、新建页面,复用同一个页面对象可以大幅降低性能消耗。
- 避免任务堆叠:如果单次页面加载+爬取的耗时可能超过5秒,不建议直接使用固定间隔的
setInterval,可以改成爬取流程结束后延迟5秒再发起下一次请求的递归写法,避免多个爬取任务同时运行占用资源。
内容的提问来源于stack exchange,提问作者merkezci
相关产品推荐
相关产品推荐

