You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确触发事件?使用Node.js Event模块实现网站新帖子监听

Node.js 爬虫监听新帖问题解决方案

1. 非阻塞轮询的实现方法

Node.js 内置的 setInterval 是异步非阻塞 API,由事件循环统一调度,完全不会阻塞脚本内其他同步代码、异步IO任务的运行,你可以直接基于 EventEmitter 封装监听器实现需求,参考代码如下:

const { EventEmitter } = require('events');
const axios = require('axios');
const cheerio = require('cheerio'); // 用于解析页面DOM结构

class PostMonitor extends EventEmitter {
  /**
   * @param {number} pollInterval 轮询间隔,单位毫秒,默认5分钟
   * @param {string} targetUrl 目标站点帖子列表页地址
   */
  constructor(pollInterval = 5 * 60 * 1000, targetUrl) {
    super();
    this.pollInterval = pollInterval;
    this.targetUrl = targetUrl;
    this.lastPostId = null; // 存储已抓取的最新帖子的唯一标识
    this.timer = null;
  }

  // 单次抓取最新帖子逻辑
  async fetchLatestPost() {
    try {
      const response = await axios.get(this.targetUrl, {
        headers: {
          // 模拟浏览器UA,降低被反爬拦截概率
          'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'
        }
      });
      const $ = cheerio.load(response.data);
      // 以下逻辑根据目标站点的DOM结构自行调整
      const latestPostEl = $('.post-list .post-item').first();
      const currentPostId = latestPostEl.attr('data-post-id');
      const postTitle = latestPostEl.find('.post-title').text().trim();
      const postUrl = latestPostEl.find('.post-title a').attr('href');

      return { currentPostId, postTitle, postUrl };
    } catch (err) {
      this.emit('error', err);
      return null;
    }
  }

  // 启动监控
  start() {
    // 启动时先执行一次抓取初始化基准值,避免首次启动就触发旧帖的新帖事件
    this.fetchLatestPost().then(post => {
      if (post) this.lastPostId = post.currentPostId;
    });

    // 启动非阻塞轮询
    this.timer = setInterval(async () => {
      const latestPost = await this.fetchLatestPost();
      if (!latestPost) return;
      // 检测到新帖子触发事件
      if (latestPost.currentPostId !== this.lastPostId) {
        this.lastPostId = latestPost.currentPostId;
        this.emit('new-post', latestPost);
      }
    }, this.pollInterval);
  }

  // 停止监控
  stop() {
    if (this.timer) clearInterval(this.timer);
  }
}

// 使用示例
const monitor = new PostMonitor(3 * 60 * 1000, 'https://目标站点地址/posts');
// 监听新帖事件,后续处理逻辑直接写在回调里即可
monitor.on('new-post', (post) => {
  console.log('检测到新帖子:', post.postTitle, post.postUrl);
  // 你的后续业务逻辑直接在这里执行,无需等待轮询间隔
});
// 监听请求错误
monitor.on('error', (err) => {
  console.error('抓取请求出错:', err.message);
});
// 启动监控
monitor.start();

// 这里的其他代码完全不受轮询影响,会正常执行
console.log('帖子监控已启动');

2. 无需等待轮询间隔推进后续流程的实现逻辑

你不需要把后续业务逻辑和轮询时序绑定,只需要把新帖检测逻辑放在定时器中,检测到更新时通过EventEmitter触发自定义事件,后续业务逻辑绑定对应事件的回调即可,事件触发是实时的,不需要等待下一次轮询间隔。如果担心服务启动后要等一个间隔才会执行第一次检测,可以在start方法中先执行一次抓取逻辑初始化基准值,不需要等待第一次定时器触发。

3. 无官方推送渠道的站点新帖监听方案说明

如果目标站点没有提供Webhook、WebSocket、RSS订阅这类主动推送更新的途径,定时轮询就是唯一可行的实现方案,没有其他更标准的处理方式。

4. 定时检测方案的合理性说明

定时轮询不属于不良实践,是无推送渠道场景下的行业通用实现,只需要注意以下几点即可:

  • 轮询间隔根据你的时效性需求合理设置,不要设置过短给目标站点造成不必要的压力,也避免被反爬策略拦截,普通场景建议设置为5分钟以上
  • 所有请求逻辑增加异常捕获,避免单次请求报错导致整个监控服务中断
  • 建议携带合理的User-Agent请求头,模拟正常浏览器访问行为
  • 可以本地持久化已经处理过的帖子标识,避免服务重启后重复触发新帖事件

内容的提问来源于stack exchange,提问作者LPepe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.18 15:45:10