# db_sp_agent **Repository Path**: mui-pear/db_sp_agent ## Basic Information - **Project Name**: db_sp_agent - **Description**: awwwwwwwwwwwwww - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-04 - **Last Updated**: 2026-05-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 江西省教育考试院公告实时监控系统 > 多 Agent 协同的智能爬虫系统,实时监控 [jxeea.cn](https://www.jxeea.cn/) 公告变更,自动分类、提取关键词、OCR 识别,存入 MySQL 并生成 PPT 报告。 --- ## 功能特性 ### 1. 公告监控与采集 - 多栏目并发爬取,自动发现新公告链接 - 指数退避重试机制,网络抖动自动恢复 - User-Agent 轮换 + 随机延迟,反爬对抗 - 代理池支持,被封自动切换 - SHA256 内容哈希变更检测 ### 2. 智能分类与关键词 - LLM 多 Provider 语义分类(DeepSeek / GLM-4 / 通义千问 / Moonshot / SiliconFlow 自动切换) - jieba TF-IDF 关键词提取 - 低置信度分类自动标记人工审核 - 基于来源栏目的快速分类回退 ### 3. OCR 图片识别 - PaddleOCR 引擎封装,支持中文识别 - 图像预处理(自适应二值化 + 去噪) - 低置信度预警,Dashboard 高亮标记 - OCR 文本自动合并到正文 ### 4. 变更通知 - 新公告 / 内容更新实时通知 - **批量合并邮件**:每轮爬取结束发送 1 封汇总邮件(非逐条发送),包含所有新增和更新公告的链接、分类 - 邮件通知(SMTP,支持 QQ / 163 / Gmail 等) - Webhook 通知(支持钉钉/飞书/企业微信) - 结构化 JSON 变更日志 ### 5. Web Dashboard - 仪表盘:公告总数、分类统计、最近公告、变更记录 - 公告列表:分页、分类筛选、关键词搜索 - 公告详情:正文查看、关键词、OCR 结果、变更历史 - 内容变更 Diff 视图,新旧版本对比 - 待审核公告列表 - **可视化操作按钮**:导航栏一键触发全量爬取 / 更新检查 / 丢弃旧公告 - **审核模式开关**:Web 界面实时切换 LLM 分类,方便调试 - **过期公告丢弃**:可配置 `DISCARD_DAYS`,超过 N 天未更新的公告可一键清除 - 后台任务轮询:按钮脉冲动画 + Toast 通知 + 自动刷新 - 健康检查端点 `/health` - Token 认证保护 API 接口(Token 自动缓存在浏览器 localStorage) - XSS 防护(markupsafe) - 详细技术报告见 [报告.md](./报告.md) ### 6. 一键部署 - Docker Compose 一键启动 MySQL + 爬虫 + Dashboard - `.env.example` 配置模板 - 独立可测试的 Agent 消息通信架构 - 优雅关闭(SIGTERM) ### 7. 数据库 - MySQL 8.0 + SQLAlchemy ORM (5 张表) - Session 生命周期管理,减少连接开销 - 数据去重(URL + 内容哈希) - 预设查询视图 ### 8. PPT 报告生成 - 自动生成 11 页 PPT 监控报告 - 封面 → 背景 → 架构 → 统计 → 关键词 → 分类 → OCR → 进化 → 总结 ### 9. 日志与运维 - RotatingFileHandler 日志轮转(10MB × 5) - 结构化日志输出 - 可扩展分布式部署架构 ## 环境要求 | 组件 | 版本 | 说明 | |------|------|------| | Python | 3.10+ | conda 环境 `db`,或 `venv`/`pip` | | MySQL | 8.0 | Docker 运行或本地安装 | | PaddleOCR | 2.9.1 | 需 paddlepaddle 2.6.2 | | DeepSeek API | - | 可选,无 API 时回退本地 TF-IDF | | 操作系统 | Linux / Windows / macOS | 全平台兼容,路径使用 `os.path.join` 自动适配 | --- ## 快速开始 ### Docker Compose 一键部署(推荐) ```bash # 1. 复制环境变量模板 cp .env.example .env # 编辑 .env 填入 API Key # 2. 一键启动 docker compose up -d # 3. 访问 Dashboard # http://127.0.0.1:5000 ``` ### 手动部署 #### 1. 启动 MySQL ```bash docker run -d --name mysql -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ -e MYSQL_DATABASE=jxeea \ mysql:8.0 ``` #### 2. 创建 conda 环境并安装依赖 ```bash conda create -n db python=3.10 -y conda activate db pip install -r requirements.txt ``` #### 3. 配置环境变量 ```bash cp .env.example .env # 编辑 .env 填入配置 ``` 关键配置项: ```bash # MySQL(必填) MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 MYSQL_USER=root MYSQL_PASSWORD=123456 MYSQL_DATABASE=jxeea # LLM API(可选,不填则使用本地 TF-IDF) DEEPSEEK_API_KEY=sk-your-key-here # 启用 LLM 语义分类 ENABLE_LLM_CLASSIFY=true # 爬虫配置 CRAWL_INTERVAL_SECONDS=3600 CRAWL_SEMAPHORE_LIMIT=5 REQUEST_DELAY=3 # Dashboard DASHBOARD_TOKEN=your-secret-token DISCARD_DAYS=90 # 邮件通知(可选) SMTP_HOST=smtp.example.com SMTP_USER=user@example.com SMTP_PASSWORD=your-password NOTIFY_EMAIL=admin@example.com ``` #### 4. 初始化数据库 ```bash python -c "from db.connection import init_db; init_db()" ``` #### 5. 运行 ```bash # 全量爬取(一次性) python main.py # 监控模式(持续运行,定时爬取) python main.py monitor # Web Dashboard(另一个终端) python web/run_dashboard.py # 浏览器打开 http://127.0.0.1:5000 ``` --- ## 数据库视图 进入 MySQL 后可使用预设视图快速查看数据: ```bash docker exec -it mysql mysql -uroot -p123456 --default-character-set=utf8mb4 jxeea ``` ```sql -- 全部公告(按时间倒序) SELECT * FROM v_latest_announcements; -- 新增公告 SELECT * FROM v_new_announcements; -- 变更过的公告 SELECT * FROM v_updated_announcements; -- 分类统计 SELECT * FROM v_by_category; -- 公告+关键词关联 SELECT * FROM v_announcement_keywords; -- 最近变更历史 SELECT * FROM v_recent_changes; ``` --- ## Web Dashboard 使用指南 访问 `http://127.0.0.1:5000` 即可打开仪表盘。 ### 导航栏操作按钮 | 按钮 | 功能 | 说明 | |------|------|------| | **爬取** | 全量爬取 | 扫描所有栏目列表页,发现新公告并检查已有公告变更 | | **更新检查** | 增量更新 | 只重新检查数据库中已有文章的内容变化,速度更快 | | **丢弃旧公告** | 清理过期公告 | 删除超过 `DISCARD_DAYS`(默认90天)未更新的公告,清理数据库空间 | | **审核模式** | 切换开关 | 开启后强制启用 LLM 语义分类,关闭则使用本地 TF-IDF,方便对比调试 | - 点击爬取/更新按钮后触发后台任务,按钮显示脉冲动画和"爬取中..." - 任务完成后弹出 Toast 通知并自动刷新页面 - 首次使用需输入 Token(保存在浏览器 localStorage),默认值见 `.env` 中 `DASHBOARD_TOKEN` ### API 端点 | 方法 | 路径 | 功能 | 认证 | |------|------|------|------| | GET | `/` | 仪表盘 | 无 | | GET | `/announcements` | 公告列表(分页/搜索/筛选) | 无 | | GET | `/announcements/` | 公告详情 | 无 | | GET | `/announcements//diff/` | 内容变更对比 | 无 | | GET | `/changes` | 变更记录列表 | 无 | | GET | `/needs_review` | 待审核公告 | 无 | | GET | `/health` | 健康检查 JSON | 无 | | GET | `/api/stats` | 统计 JSON | 无 | | POST | `/api/crawl` | 触发全量爬取 | Token | | POST | `/api/update` | 触发更新检查 | Token | | POST | `/api/review-toggle` | 切换审核模式 | Token | | POST | `/api/discard` | 丢弃过期公告 | Token | | GET | `/api/crawl-status` | 查询后台任务状态 | 无 | API 调用示例(需要 Token): ```bash # 触发爬取 curl -X POST http://127.0.0.1:5000/api/crawl \ -H "X-API-Token: admin123" # 触发更新检查 curl -X POST http://127.0.0.1:5000/api/update \ -H "X-API-Token: admin123" # 开启审核模式 curl -X POST http://127.0.0.1:5000/api/review-toggle \ -H "Content-Type: application/json" \ -H "X-API-Token: admin123" \ -d '{"enabled": true}' # 丢弃超过 DISCARD_DAYS 天未更新的公告 curl -X POST http://127.0.0.1:5000/api/discard \ -H "X-API-Token: admin123" # 查询任务状态 curl http://127.0.0.1:5000/api/crawl-status ``` --- ## 项目结构 ``` D:\code\db\ ├── main.py # 程序入口(全量爬取/监控模式 + 优雅关闭) ├── run.py # 一键启动交互脚本 ├── requirements.txt # Python 依赖 ├── Dockerfile # Docker 镜像 ├── docker-compose.yml # Docker Compose 一键部署 ├── .env # 环境变量配置 ├── .env.example # 环境变量模板 ├── .gitignore │ ├── README.md # 本文件 ├── 计划.md # 完整实施计划 ├── stage1.md # Stage 1 总结 ├── stage2.md # Stage 2 总结 ├── stage3.md # Stage 3 总结 ├── 优化.md # 优化路线图 │ ├── config/ │ ├── settings.py # 全局配置(含 cached_property) │ ├── target_sites.py # 目标网站配置(栏目/选择器) │ └── evolution_rules.json # 自进化规则 │ ├── agents/ │ ├── base_agent.py # Agent 基类(消息队列通信) │ ├── coordinator.py # 总调度(并发爬取 + 消息通信 + 自进化) │ ├── crawler.py # 爬虫(指数退避 + UA轮换 + 代理池) │ ├── storage.py # 存储(MySQL ORM 持久化 + 错误处理) │ ├── parser.py # 解析(LLM分类/jieba关键词 + 低置信度审核) │ ├── reporter.py # 报告(PPT 自动生成) │ └── ocr_agent.py # OCR(OCRPipeline 薄封装) │ ├── db/ │ ├── models.py # SQLAlchemy ORM 模型(5张表) │ ├── connection.py # 数据库连接管理(session_scope) │ └── migrations/init.sql # 原生建表 SQL │ ├── utils/ │ ├── hash_utils.py # SHA256 内容哈希(None 安全) │ ├── text_cleaner.py # HTML 清洗/日期解析 │ ├── keyword_extractor.py # jieba TF-IDF 关键词(类常量 stopwords) │ ├── llm_client.py # 多Provider LLM 客户端(5 Provider + 退避) │ ├── notifier.py # 变更通知(Email + Webhook + 错误处理) │ └── ocr_pipeline.py # OCR 管道(下载 → 预处理 → 识别 → 存储) │ ├── ocr/ │ └── paddle_ocr.py # PaddleOCR 引擎封装 │ ├── web/ │ ├── __init__.py # Flask Dashboard(操作按钮/审核开关/后台任务/Token认证/XSS防护/Diff) │ └── run_dashboard.py # Dashboard 启动脚本 │ ├── scheduler/ │ └── jobs.py # 定时任务工具 │ ├── tests/ │ ├── mock_server.py # 本地模拟测试服务器 │ ├── test_target_sites.py # 测试站点配置 │ ├── test_crawler.py # 爬虫单元测试 │ ├── test_e2e.py # Stage 1 端到端测试 │ ├── test_stage2_final.py # Stage 2 集成测试 │ └── test_stage3.py # Stage 3 全流程测试 │ └── output/ ├── logs/ # 运行日志 + 通知日志(轮转) └── ppt/ # 生成的 PPT 报告 ``` --- ## 系统架构 ``` Coordinator (总调度,并发控制) │ ├── Crawler → 指数退避抓取 → UA轮换/代理池 → 随机延迟反爬 ├── Parser → LLM分类 (5 Provider自动切换) → jieba TF-IDF 关键词 ├── OCR → 图像预处理 → PaddleOCR 识别 → 低置信度预警 ├── Storage → MySQL 持久化 → 错误处理 + rollback ├── Reporter → PPT 报告自动生成 (python-pptx) └── Notifier → 控制台 + 文件日志 + Email + Webhook ``` ### LLM Provider 自动切换 ``` DeepSeek → Moonshot → SiliconFlow → GLM-4-Flash → 通义千问 → 本地 TF-IDF 回退 ✅ 备选 备选 备选 备选 总是可用 ``` --- ## PPT 报告生成 ```bash # 生成 PPT python -c " import asyncio, sys sys.path.insert(0, '.') from agents.reporter import ReporterAgent async def main(): r = ReporterAgent() result = await r._generate_ppt({'title': '公告监控报告'}) print(f'PPT 已生成: {result[\"filepath\"]}') asyncio.run(main()) " ``` PPT 输出位置:`output/ppt/jxeea_report_YYYYMMDD_HHMMSS.pptx` --- ## 运行测试 ```bash # 启动模拟服务器(另开终端) python tests/mock_server.py # 运行测试(另一个终端) python tests/test_crawler.py # 爬虫测试 python tests/test_e2e.py # 端到端测试 python tests/test_stage2_final.py # Stage 2 集成测试 python tests/test_stage3.py # Stage 3 全流程测试 ``` --- ## 常见问题 ### Q: 网站无法访问? 当前服务器可能被 jxeea.cn Geo-block。系统在本地 Windows 上运行即可正常爬取: ```bash conda activate db python main.py ``` ### Q: LLM 分类没有生效? Parser 默认使用来源栏目分类(gaokao/zhongkao 等)。如需 LLM 语义分类,在 `.env` 中配置: ```bash ENABLE_LLM_CLASSIFY=true DEEPSEEK_API_KEY=sk-your-key ``` ### Q: OCR 识别乱码? 确保使用了 `paddlepaddle==2.6.2` 和 `paddleocr==2.9.1`(版本不匹配会导致崩溃)。 ### Q: MySQL 中文乱码? 连接时加 `--default-character-set=utf8mb4`: ```bash docker exec -it mysql mysql -uroot -p123456 --default-character-set=utf8mb4 jxeea ``` ### Q: 如何查看实时日志? ```bash tail -f output/logs/crawler.log # 爬虫日志 tail -f output/logs/notifications.log # 通知日志 tail -f output/logs/changes.log # 变更日志(JSON 格式) ``` ### Q: Dashboard 如何开启认证? 在 `.env` 中设置: ```bash DASHBOARD_TOKEN=your-secret-token ``` API 调用时在 Header 中添加 `X-API-Token: your-secret-token`。 --- ## 技术栈 | 层 | 技术 | |----|------| | 爬虫 | httpx + BeautifulSoup4 + lxml | | 变更检测 | SHA256 内容哈希 | | 数据库 | MySQL 8.0 + SQLAlchemy ORM | | LLM | DeepSeek / Moonshot / SiliconFlow / GLM-4 / 通义千问 (OpenAI 兼容) | | 关键词 | jieba TF-IDF + scikit-learn | | OCR | PaddleOCR 2.9.1 (PP-OCRv4) + OpenCV 预处理 | | 调度 | APScheduler | | PPT | python-pptx | | Web | Flask + Jinja2 + markupsafe | | 部署 | Docker Compose | | 环境 | conda (Python 3.10) |