Node.js 实现定时爬虫:从手动触发到自动调度

作者:栖木

时间:2026-10-24

浏览:157

本文介绍如何使用 Node.js 构建定时爬虫。针对 B站舰长列表抓取场景,通过 Koa2 搭建服务并引入节流机制解决反爬问题。核心利用 node-schedule 模块配置 cron 规则,实现每分钟自动执行抓取任务,兼顾效率与稳定性,提供完整的代码示例与配置说明。

文章目录

事件起因

近期需要协助朋友审核 B站舰长群,逐一检查舰长列表显然不符合程序员的效率原则。将任务交给计算机自动处理,既能保证准确性,又能腾出时间处理其他事务。基于这一思路,开始进行代码开发。

已知获取舰长列表的 API 接口,初步方案是使用 Axios 直接请求该接口。

经过一段时间的开发,完成了名为 bilibili-live-captain-tools 1.0 的爬虫脚本。


const axios = require('axios')
const roomid = "146088"
const ruid = "642922"
const url = `https://api.live.bilibili.com/xlive/app-room/v2/guardTab/topList?roomid=${roomid}&ruid=${ruid}&page_size=30`

const Captin = {
 1: '总督',
 2: '提督',
 3: '舰长'
}

const reqPromise = url => axios.get(url);

let CaptinList = []
let UserList = []

async function crawler(URL, pageNow) {
 const res = await reqPromise(URL);
 if (pageNow == 1) {
 CaptinList = CaptinList.concat(res.data.data.top3);
 }
 CaptinList = CaptinList.concat(res.data.data.list);
}


function getMaxPage(res) {

 const Info = res.data.data.info
 const { page: maxPage } = Info
 return maxPage
}


function getUserList(res) {

 for (let item of res) {
 const userInfo = item
 const { uid, username, guard_level } = userInfo
 UserList.push({ uid, username, Captin: Captin[guard_level] })
 }
}

async function main(UID) {
 const maxPage = await reqPromise(`${url}&page=1`).then(getMaxPage)
 for (let pageNow = 1; pageNow < maxPage + 1; pageNow++) {
 const URL = `${url}&page=${pageNow}`;
 await crawler(URL, pageNow);
 }
 getUserList(CaptinList)
 console.log(search(UID, UserList))
 return search(UID, UserList)
}

function search(uid, UserList) {
 for (let i = 0; i < UserList.length; i++) {
 if (UserList[i].uid === uid) {
 return UserList[i];
 }
 }
 return 0
}

module.exports = {
 main
}

该版本爬虫仅支持手动触发,运行需要命令行环境和 Node.js 支持。为了便于操作,使用 Koa2 搭建了一个简单的 Web 服务页面。


const Koa = require('koa');
const app = new Koa();
const path = require('path')
const fs = require('fs');
const router = require('koa-router')();
const index = require('./index')
const views = require('koa-views')



app.use(views(path.join(__dirname, './'), {
 extension: 'ejs'
}))
app.use(router.routes());

router.get('/', async ctx => {
 ctx.response.type = 'html';
 ctx.response.body = fs.createReadStream('./index.html');
})

router.get('/api/captin', async (ctx) => {
 const UID = ctx.request.query.uid
 console.log(UID)
 const Info = await index.main(parseInt(UID))
 await ctx.render('index', {
 Info,
 })
});

app.listen(3000);

由于页面未实现节流或防抖机制,且当前版本仅支持实时抓取,等待时间较长。频繁刷新页面容易触发 B站的反爬虫机制,导致服务器 IP 被风控。

针对上述问题,bilibili-live-captain-tools 2.0 版本应运而生。


function throttle(fn, delay) {
 var timer;
 return function () {
 var _this = this;
 var args = arguments;
 if (timer) {
  return;
 }
 timer = setTimeout(function () {
  fn.apply(_this, args);
  timer = null; // 在delay后执行完fn之后清空timer,此时timer为假,throttle触发可以进入计时器
 }, delay)
 }
}

新版本在增加节流防抖逻辑的同时,采用了伪实时抓取策略,即通过定时任务每分钟执行一次爬取操作。

为了实现定时执行,考虑利用 Egg 框架的 schedule 功能。但为了避免爬虫程序过度依赖框架特性,经过调研后选择了更通用的方案。

使用 Node Schedule 实现定时任务

Node Schedule 是 Node.js 中灵活的 cron 类及非 cron 类作业调度程序。它允许使用可选的重复规则来计划作业(任意函数),以在特定日期执行。它在任何给定时间仅使用一个计时器,而非每秒或每分钟重新评估即将到来的作业。

一、安装 node-schedule


npm install node-schedule
# 或
yarn add node-schedule

二、基本用法

首先查看官方提供的示例代码。


const schedule = require('node-schedule');

const job = schedule.scheduleJob('42 * * * *', function(){
 console.log('The answer to life, the universe, and everything!');
});

schedule.scheduleJob 的第一个参数需要按照特定规则输入。

Node Schedule 规则格式如下表所示。

* * * * * * ┬ ┬ ┬ ┬ ┬ ┬ │ │ │ │ │ | │ │ │ │ │ └ 星期几,取值:0 - 7,其中 0 和 7 都表示是周日 │ │ │ │ └─── 月份,取值:1 - 12 │ │ │ └────── 日期,取值:1 - 31 │ │ └───────── 时,取值:0 - 23 │ └──────────── 分,取值:0 - 59 └─────────────── 秒,取值:0 - 59(可选) 也可以指定一个具体的时间,如:const date = new Date()

理解规则后,我们可以自行实现一个定时任务。


const schedule = require('node-schedule');

// 定义一个时间
let date = new Date(2021, 3, 10, 12, 00, 0);

// 定义一个任务
let job = schedule.scheduleJob(date, () => {
 console.log("现在时间:",new Date());
});

上述示例表示在 2021 年 3 月 10 日 12 点整执行报时操作。

三、高级用法

除了基础用法,还可以使用更灵活的方法来实现定时任务。

3.1、隔一分钟执行一次


const schedule = require('node-schedule');

// 定义规则
let rule = new schedule.RecurrenceRule();
rule.second = 0
//每分钟 0 秒执行一次

// 启动任务
let job = schedule.scheduleJob(rule, () => {
 console.log(new Date());
});

rule 支持设置的值包括 second、minute、hour、date、dayOfWeek、month、year 等。

一些常见的规则配置如下表所示。

每秒执行 rule.second = [0,1,2,3......59]; 每分钟 0 秒执行 rule.second = 0; 每小时 30 分执行 rule.minute = 30; rule.second = 0; 每天 0 点执行 rule.hour =0; rule.minute =0; rule.second =0; 每月 1 号的 10 点执行 rule.date = 1; rule.hour = 10; rule.minute = 0; rule.second = 0; 每周一、周三、周五的 0 点和 12 点执行 rule.dayOfWeek = [1,3,5]; rule.hour = [0,12]; rule.minute = 0; rule.second = 0;

四、终止任务

可以使用 cancel() 方法终止一个正在运行的任务。当任务出现异常时,应及时取消并终止任务。


job.cancel();

总结

node-schedule 是 Node.js 的一个定时任务(crontab)模块。我们可以使用定时任务来对服务器系统进行维护,让其在固定的时间段执行某些必要的操作,还可以使用定时任务发送邮件、爬取数据等。

到此这篇关于 Nodejs 实现定时爬虫的文章就介绍到这了,更多相关 Nodejs 定时爬虫内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章