如何在Node.js中实现按周期分批执行每日项目数据抓取任务?
实现按日期分配的循环抓取任务
核心方案
用定时任务库每日触发执行,结合星期判断匹配对应项目列表,从MongoDB拉取项目配置后执行抓取,同时控制并发避免负载过高。
具体实现步骤
1. 依赖选择
用Node.js生态里的定时任务库,比如node-schedule(语法更直观)或node-cron(类似Linux cron表达式),二选一即可。同时确保你已经集成了MongoDB驱动(比如mongoose或官方mongodb包)。
2. 定义日期-项目映射规则
先把你的抓取规则写成配置对象,注意不同库的星期编号:
node-schedule中,周日是0,周一1,周二2,周四4,周五5node-cron中,周日是0或7,周一1,依此类推
示例规则:
const scheduleRules = { 1: ['Proj-1', 'Proj-2'], // 周一 4: ['Proj-1', 'Proj-2'], // 周四 2: ['Proj-3', 'Proj-4'], // 周二 5: ['Proj-3', 'Proj-4'] // 周五 };
3. 配置每日定时任务
以node-schedule为例,设置每天凌晨2点触发任务(可根据业务调整时间):
const schedule = require('node-schedule'); const mongoose = require('mongoose'); // 连接MongoDB(假设你的项目模型是Project) mongoose.connect('mongodb://localhost:27017/your-db-name'); const Project = mongoose.model('Project', { name: String, crawlConfig: Object, // 存储抓取所需的URL、参数等 lastCrawled: Date }); // 每天凌晨2点执行 const job = schedule.scheduleJob('0 0 2 * * *', async () => { const today = new Date(); const dayOfWeek = today.getDay(); // 0=周日,1=周一... // 匹配当天要抓取的项目列表 const targetProjects = scheduleRules[dayOfWeek]; if (!targetProjects) { console.log('今日无抓取任务'); return; } try { // 从MongoDB查询目标项目 const projects = await Project.find({ name: { $in: targetProjects } }); if (projects.length === 0) { console.log('未找到匹配的项目配置'); return; } // 并发执行抓取(控制并发数,避免负载过高) const concurrencyLimit = 2; // 同时跑2个抓取任务,可调整 for (let i = 0; i < projects.length; i += concurrencyLimit) { const batch = projects.slice(i, i + concurrencyLimit); await Promise.all(batch.map(async (proj) => { console.log(`开始抓取 ${proj.name}`); // 调用你的抓取函数,比如await crawlProject(proj.crawlConfig); // 抓取完成后更新最后抓取时间 await Project.updateOne({ _id: proj._id }, { lastCrawled: new Date() }); console.log(`${proj.name} 抓取完成`); })); } } catch (err) { console.error('抓取任务执行失败:', err); // 可选:发送告警邮件/消息 } });
4. 关键细节处理
- 并发控制:用分批+Promise.all限制同时运行的抓取任务数,避免服务器负载过高。
- 错误处理:给每个抓取任务加try-catch,单个项目失败不影响其他任务;全局捕获异常记录日志或告警。
- 时区问题:如果服务器时区和业务时区不一致,创建Date对象时要指定时区,比如用
new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Shanghai' }))转换。 - 幂等性:抓取前检查
lastCrawled,避免同一天重复抓取(如果任务意外重启)。 - 日志与监控:记录每个任务的执行状态、耗时,方便排查问题。
替代方案:用cron表达式直接配置
如果用node-cron,可以直接为不同日期配置独立的cron任务,比如:
const cron = require('node-cron'); // 周一、周四凌晨2点执行Proj1、2 cron.schedule('0 2 * * 1,4', async () => { await runCrawl(['Proj-1', 'Proj-2']); }); // 周二、周五凌晨2点执行Proj3、4 cron.schedule('0 2 * * 2,5', async () => { await runCrawl(['Proj-3', 'Proj-4']); }); // 封装通用抓取函数 async function runCrawl(projectNames) { const projects = await Project.find({ name: { $in: projectNames } }); // 后续抓取逻辑和上面一致 }
这种方式更直观,适合规则简单的场景。
内容的提问来源于stack exchange,提问作者Abhishek Solanki
相关产品推荐
相关产品推荐

