Skip to main content
Glama
Habapure

cqupt-notice

by Habapure
README.md
# CQUPT 教务处通知爬取与 AI 推送系统

> 基于 **DrissionPage + MCP + AstrBot** 的重庆邮电大学教务处通知自动爬取、AI 分析与 QQ 推送系统。
>
> 每天早上 8 点,自动抓取教务处最新通知,由 AI 分析重要性并生成友好文案推送到你的 QQ。

---

## 📋 目录

- [系统架构](#系统架构)
- [技术选型与原理](#技术选型与原理)
- [环境要求](#环境要求)
- [快速开始](#快速开始)
  - [第一步:安装依赖](#第一步安装依赖)
  - [第二步:测试爬虫](#第二步测试爬虫)
  - [第三步:配置 MCP Server](#第三步配置-mcp-server)
  - [第四步:接入 AstrBot](#第四步接入-astrbot)
  - [第五步:设置定时推送](#第五步设置定时推送)
- [配置说明](#配置说明)
- [去重机制](#去重机制)
- [常见问题](#常见问题)
- [项目结构](#项目结构)
- [许可证](#许可证)

---

## 系统架构

```
┌─────────────────────────────────────────────────────────────┐
│                      AstrBot (QQ 机器人)                      │
│                                                             │
│  ┌──────────┐    调用工具     ┌──────────────────────┐      │
│  │ FutureTask│ ──────────────▶│  MCP Client (内置)    │      │
│  │ (定时任务) │                └──────────┬───────────┘      │
│  └──────────┘                           │                   │
│       ▲                                  │ HTTP/STDIO        │
│       │ 推送                            ▼                   │
│  ┌────┴─────┐                   ┌──────────────────┐       │
│  │  AI Agent │ ◀── 通知数据 ──── │  MCP Server       │       │
│  │ (大模型)  │                   │  (get_latest_notices) │  │
│  └────┬─────┘                   └────────┬─────────┘       │
│       │ 生成文案                          │ 爬取            │
│       ▼                                   ▼                 │
│  ┌─────────┐                   ┌──────────────────┐       │
│  │ QQ 推送  │                   │ DrissionPage     │       │
│  └─────────┘                   │ + Chrome         │       │
│                                └────────┬─────────┘       │
└─────────────────────────────────────────┼───────────────────┘
                                          ▼
                              https://jw.cqupt.edu.cn/tzgg.htm
```

### 工作流程

1. **定时触发**:AstrBot 的 FutureTask 每天 08:00 唤醒 AI Agent
2. **调用工具**:Agent 调用 MCP 工具 `get_latest_notices`
3. **爬取解析**:MCP Server 用 DrissionPage 绕过 WAF 爬取通知,解析后去重
4. **AI 分析**:大模型分析每条通知的重要性、比赛建议、关键信息
5. **QQ 推送**:生成友好文案推送到 QQ

---

## 技术选型与原理

### 为什么用 DrissionPage 而不是 requests?

目标页面 `jw.cqupt.edu.cn` 使用了 **加速乐 WAF**,首次访问会返回 JS 挑战页面(HTTP 412),
要求浏览器执行 JS 计算后才能获得真实内容。

| 方案 | 结果 | 原因 |
|------|------|------|
| `requests` / `httpx` | ❌ 失败 | 无法执行 JS,只能拿到挑战页 |
| `cloudscraper` | ❌ 失败 | 新版加速乐防护已升级,旧绕过手段失效 |
| `Playwright` | ❌ 失败 | 使用 CDP 协议,自动化特征明显,被 WAF 识别 |
| **DrissionPage + Chrome** | ✅ 成功 | 通过启动参数隐藏自动化特征 |

### DrissionPage 反检测原理

启动 Chrome 时传入两个关键参数:

```python
co.set_argument("--disable-blink-features=AutomationControlled")  # 移除 navigator.webdriver 标识
co.set_argument("--headless=new")                                  # 新版无头模式,更接近真实浏览器
```

这样 WAF 的 JS 检测脚本执行时,`navigator.webdriver` 返回 `undefined`(而非 `true`),
浏览器指纹看起来像真实用户,从而通过挑战、获得有效 Cookie。

### 为什么用 MCP?

MCP(Model Context Protocol)是 AstrBot 官方推荐的外部工具扩展方式。
它把"爬取通知"这个能力封装成标准化工具 `get_latest_notices`,让 AI Agent 可以像调用函数一样调用它。

---

## 环境要求

| 依赖 | 版本要求 | 说明 |
|------|----------|------|
| Python | >= 3.10 | |
| Chrome / Chromium | >= 100 | DrissionPage 会自动调用系统 Chrome |
| AstrBot | 最新版 | QQ 机器人框架,需支持 MCP |
| 大模型 API | 任意 | 如 河图、OpenAI、通义千问等 |

> 💡 Chrome 只需正常安装即可,DrissionPage 会自动查找。
> 如果自动查找失败,可在 `config.json` 中指定 `chrome_path`。

---

## 快速开始

### 第一步:安装依赖

```bash
# 1. 克隆本项目
git clone https://github.com/Habapure/cqupt-notice-pusher.git
cd cqupt-notice-pusher

# 2. 创建虚拟环境(推荐)
python -m venv venv

# Windows
venv\Scripts\activate

# Linux / macOS
source venv/bin/activate

# 3. 安装依赖
pip install -r requirements.txt
```

### 第二步:测试爬虫

在接入 AstrBot 之前,先独立测试爬虫是否正常工作:

```bash
# 基本测试(爬取当天通知,不标记已推送)
python tests/test_crawler.py

# 查看页面上所有通知(不按日期过滤)
python tests/test_crawler.py --all

# 有头模式(能看到浏览器操作,便于调试)
python tests/test_crawler.py --no-head

# 爬取最近 3 天的通知
python tests/test_crawler.py --days 3
```

✅ **预期输出**:

```
============================================================
  CQUPT 教务处通知爬虫 - 独立测试
============================================================
目标 URL : https://jw.cqupt.edu.cn/tzgg.htm
无头模式 : True
日期范围 : 最近 1 天
============================================================

[1/4] 正在爬取页面...
✅ 爬取成功,HTML 长度: 12345

[2/4] 正在解析通知列表...
✅ 解析到 20 条通知

[3/4] 日期过滤(最近 1 天)...
✅ 过滤后剩余 3 条通知

[4/4] 去重检查...
   已推送记录: 0 条
✅ 去重后剩余 3 条新通知

============================================================
  爬取结果
============================================================

1. [2026-09-21] 🆕 新
   标题: 关于XXX的通知
   链接: https://jw.cqupt.edu.cn/info/1012/69051.htm

...
```

> ⚠️ 如果爬虫失败,请先参考 [常见问题](#常见问题) 排查。

### 第三步:配置 MCP Server

1. 复制配置文件模板:

```bash
cp config.example.json config.json
```

2. 编辑 `config.json`(通常无需修改,默认即可):

```json
{
  "target_url": "https://jw.cqupt.edu.cn/tzgg.htm",
  "days_to_fetch": 1,
  "headless": true,
  "chrome_path": null,
  "record_file": "pushed_records.json",
  "page_load_timeout": 30
}
```

3. 测试 MCP Server 能否正常启动:

```bash
# 测试爬取功能(不启动 MCP 服务)
python mcp_server.py --no-mark

# 测试 MCP Server 能否启动(stdio 模式,会阻塞,Ctrl+C 退出)
python mcp_server.py mcp
```

MCP Server 支持两种传输模式:

| 模式 | 命令 | 适用场景 |
|------|------|----------|
| **stdio**(默认) | `python mcp_server.py mcp` | AstrBot 和爬虫在同一台机器 |
| **streamable-http** | `python mcp_server.py mcp --transport streamable-http --port 8000` | AstrBot 和爬虫在不同机器 |

### 第四步:接入 AstrBot

> 📖 **完整图文指南见 [`astrbot/future_task_guide.md`](astrbot/future_task_guide.md)**,以下是快速版。

#### 方式 A:stdio 传输(同机部署,推荐)

1. 确保 AstrBot 已安装并能正常运行

2. 在 AstrBot 管理后台 → MCP 管理 → 添加 MCP Server:

   | 字段 | 值 |
   |------|-----|
   | 名称 | `cqupt-notice` |
   | 传输方式 | `stdio` |
   | 启动命令 | `python`(或虚拟环境 python 的绝对路径) |
   | 命令参数 | `["/你的绝对路径/mcp_server.py", "mcp"]` |

   > ⚠️ 路径必须是**绝对路径**
   > - Windows: `E:\projects\cqupt-notice-pusher\mcp_server.py`
   > - Linux: `/home/user/cqupt-notice-pusher/mcp_server.py`
   >
   > 如果用了虚拟环境,启动命令写虚拟环境的 python:
   > - Windows: `E:\projects\cqupt-notice-pusher\venv\Scripts\python.exe`
   > - Linux: `/home/user/cqupt-notice-pusher/venv/bin/python`

3. 保存并连接

#### 方式 B:streamable-http 传输(跨机器部署)

1. 在 MCP Server 所在机器启动:
   ```bash
   python mcp_server.py mcp --transport streamable-http --host 0.0.0.0 --port 8000
   ```

2. 在 AstrBot 管理后台添加 MCP Server:

   | 字段 | 值 |
   |------|-----|
   | 名称 | `cqupt-notice` |
   | 传输方式 | `streamable-http` |
   | URL | `http://MCP服务器IP:8000/mcp` |

3. 保存并连接

#### 验证连接成功

在 AstrBot 日志中看到以下内容即表示成功:

```
✅ MCP 服务器 cqupt-notice 连接成功
已注册工具: get_latest_notices
已注册工具: get_notice_count
```

### 第五步:设置定时推送

1. 在 AstrBot 管理后台进入「主动任务」/「FutureTask」页面

2. 新建任务:

   | 字段 | 值 |
   |------|-----|
   | 任务名称 | `重邮教务处通知早报` |
   | 执行时间 | 每天 `08:00` |
   | 投递目标 | 你的 QQ(私聊或群聊) |
   | 系统提示词 | 见下方 |

3. 系统提示词(复制 [`astrbot/system_prompt.md`](astrbot/system_prompt.md) 的内容):

   ```
   你是一个「重庆邮电大学教务处通知推送助手」。你的职责是每天定时获取教务处最新通知,
   分析每条通知的重要性,并以友好、简洁的格式推送给用户。

   工作流程:
   1. 调用 MCP 工具 get_latest_notices 获取当天的最新通知列表
   2. 对每条通知分析:重要程度(高/中/低)、比赛建议、关键信息
   3. 按固定格式生成推送文案

   推送文案格式:
   🌅 早安!今天是 X 月 X 日,以下是教务处最新通知:

   📋 通知1:《通知标题》
      重要程度:⭐⭐⭐
      比赛建议:值得参加 / 不建议参加
      截止日期:XXXX-XX-XX
      🔗 原文链接:https://...

   —— 重邮教务处通知早报
   ```

4. 保存并启用任务

5. **测试**:点击「立即执行」,你的 QQ 应收到推送消息 🎉

---

## 配置说明

`config.json` 各字段说明:

| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `target_url` | string | `https://jw.cqupt.edu.cn/tzgg.htm` | 通知公告页 URL |
| `days_to_fetch` | int | `1` | 抓取最近 N 天的通知(1 = 仅当天) |
| `headless` | bool | `true` | 是否无头模式运行 Chrome |
| `chrome_path` | string/null | `null` | Chrome 可执行文件路径,null 为自动查找 |
| `record_file` | string | `pushed_records.json` | 已推送记录文件路径 |
| `page_load_timeout` | int | `30` | 页面加载超时(秒) |

---

## 去重机制

为避免重复推送同一条通知,系统维护一份已推送记录文件 `pushed_records.json`:

- 每次爬取后,对比通知 URL 是否已推送过
- 只返回**未推送过**的通知
- `get_latest_notices` 被调用后,自动将返回的通知标记为已推送

### 重置去重记录

如果需要重新推送所有通知,删除 `pushed_records.json` 即可:

```bash
rm pushed_records.json   # Linux / macOS
del pushed_records.json  # Windows
```

---

## 常见问题

### Q: 爬虫返回空列表 / 爬取失败

**可能原因 & 解决方案:**

1. **WAF 拦截**
   - 将 `config.json` 中 `headless` 设为 `false`,观察浏览器是否被拦截
   - 确认 Chrome 版本 >= 100

2. **页面结构变化**
   - 教务处改版后 HTML 结构可能变化
   - 用 `python tests/test_crawler.py --no-head` 打开浏览器检查页面
   - 根据实际 HTML 调整 `mcp_server.py` 中的 `parse_notices` 函数

3. **网络问题**
   - 确认服务器能正常访问 `jw.cqupt.edu.cn`
   - `curl https://jw.cqupt.edu.cn/tzgg.htm` 测试连通性

### Q: MCP 工具在 AstrBot 中找不到

1. 先在命令行运行 `python mcp_server.py` 确认脚本无报错
2. 检查 AstrBot MCP 配置中的路径是否为**绝对路径**
3. 检查 AstrBot 日志中是否有 MCP 连接错误
4. 确认 `mcp` 包已安装:`pip show mcp`

### Q: 推送内容为空

这是正常现象。如果当天没有新通知,或所有通知都已推送过,AI 会回复
"今天教务处没有新通知哦~"。

可删除 `pushed_records.json` 后重新测试。

### Q: DrissionPage 找不到 Chrome

在 `config.json` 中手动指定 Chrome 路径:

```json
{
  "chrome_path": "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
}
```

常见 Chrome 路径:
- Windows: `C:\Program Files\Google\Chrome\Application\chrome.exe`
- macOS: `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`
- Linux: `/usr/bin/google-chrome` 或 `/usr/bin/chromium-browser`

---

## 项目结构

```
cqupt-notice-pusher/
├── README.md                    # 本文件,完整教程
├── mcp_server.py                # MCP Server 主程序(爬虫 + 解析 + 去重 + 工具)
├── requirements.txt             # Python 依赖
├── config.example.json          # 配置模板(复制为 config.json 使用)
├── .gitignore
├── astrbot/
│   ├── system_prompt.md         # AstrBot AI Agent 系统提示词
│   └── future_task_guide.md     # FutureTask 详细配置指南
└── tests/
    └── test_crawler.py          # 独立爬虫测试脚本
```

---

## 许可证

MIT License

---

## 致谢

- [DrissionPage](https://github.com/g1879/DrissionPage) - 强大的 Python 浏览器自动化库
- [AstrBot](https://github.com/Soulter/AstrBot) - 多平台 QQ 机器人框架
- [MCP](https://modelcontextprotocol.io/) - Model Context Protocol

> 本项目仅供学习交流使用,请遵守学校网站的使用条款,不要对目标网站造成过大压力。

Maintenance

ActivityMaintained
ResponsivenessUnresponsive