# 招聘数据增量采集与消息队列服务技术方案 ## 1. 项目概述 ### 1.1 需求背景 从八爪鱼API采集招聘数据,筛选近7天发布的数据,通过RabbitMQ消息队列提供数据消费接口,支持消息级别TTL自动过期。 ### 1.2 核心功能 - 增量采集八爪鱼API招聘数据(从后往前采集,最新数据优先) - 日期过滤(发布日期 + 采集时间均在7天内) - RabbitMQ消息队列(支持消息TTL,7天自动过期) - 容器启动自动开始采集 - 提供REST API消费接口 --- ## 2. 系统架构 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 系统架构图 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ 八爪鱼API │───▶│ 采集服务 │───▶│ 日期过滤器 │ │ │ │ (数据源) │ │ (从后往前) │ │ (7天内数据) │ │ │ └──────────────┘ └──────────────┘ └────────┬─────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ RabbitMQ 服务 │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ │ │ Queue: job_data │ │ │ │ │ │ - 消息TTL: 7天 (604800000ms) │ │ │ │ │ │ - 过期消息自动删除 │ │ │ │ │ │ - 持久化存储 │ │ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ FastAPI 服务 │ │ │ │ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ │ │ │ GET /consume │ │ GET /status │ │ │ │ │ │ (消费数据) │ │ (采集状态/进度) │ │ │ │ │ └─────────────────┘ └─────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 3. 技术选型 | 组件 | 技术方案 | 版本 | 说明 | |------|---------|------|------| | 运行环境 | Python | 3.11+ | 主开发语言 | | HTTP客户端 | httpx | 0.27+ | 异步HTTP请求 | | 消息队列 | RabbitMQ | 3.12+ | 支持消息级别TTL | | MQ客户端 | pika | 1.3+ | Python RabbitMQ SDK | | API框架 | FastAPI | 0.109+ | REST接口 | | 容器编排 | Docker Compose | 2.0+ | 服务部署 | | 数据存储 | SQLite | 内置 | 存储采集进度 | --- ## 4. 项目结构 ``` job_crawler/ ├── app/ # 应用代码 │ ├── api/ # API路由层 │ │ ├── __init__.py │ │ └── routes.py # 路由定义 │ ├── core/ # 核心配置 │ │ ├── __init__.py │ │ ├── config.py # 配置管理 │ │ └── logging.py # 日志配置 │ ├── models/ # 数据模型 │ │ ├── __init__.py │ │ ├── job.py # 招聘数据模型 │ │ ├── progress.py # 进度模型 │ │ └── response.py # 响应模型 │ ├── services/ # 业务服务层 │ │ ├── __init__.py │ │ ├── api_client.py # 八爪鱼API客户端 │ │ ├── crawler.py # 采集核心逻辑 │ │ ├── rabbitmq_service.py # RabbitMQ服务 │ │ └── progress_store.py # 进度存储 │ ├── utils/ # 工具函数 │ │ ├── __init__.py │ │ └── date_parser.py # 日期解析 │ ├── __init__.py │ └── main.py # 应用入口 ├── config/ # 配置文件 │ ├── config.yml # 运行配置 │ └── config.yml.docker # Docker配置模板 ├── docker-compose.yml # 容器编排 ├── Dockerfile # 应用镜像构建 ├── deploy.sh # 部署脚本(Linux) ├── deploy.bat # 部署脚本(Windows) ├── requirements.txt # Python依赖 └── README.md # 使用说明 ``` --- ## 5. 核心模块设计 ### 5.1 增量采集模块 #### 采集策略(从后往前) ```python # 增量采集流程 1. 获取数据总数 total 2. 读取上次采集的起始位置 last_start_offset 3. 计算本次采集范围: - start_offset = total - batch_size (从最新数据开始) - end_offset = last_start_offset (截止到上次位置) 4. 循环采集: offset 从 start_offset 递减到 end_offset 5. 每批数据过滤后立即发送到RabbitMQ 6. 采集完成后保存 last_start_offset = 本次起始位置 ``` #### 进度持久化 使用SQLite存储采集进度: ```sql CREATE TABLE crawl_progress ( task_id TEXT PRIMARY KEY, last_start_offset INTEGER, -- 上次采集的起始位置 total INTEGER, last_update TIMESTAMP, status TEXT, filtered_count INTEGER, produced_count INTEGER ); ``` ### 5.2 日期过滤模块 #### aae397 字段格式解析 | 原始值 | 解析规则 | 示例结果 | |--------|---------|---------| | "今天" | 当前日期 | 2026-01-15 | | "1月13日" | 当年+月日 | 2026-01-13 | | "1月9日" | 当年+月日 | 2026-01-09 | #### 过滤逻辑 ```python def is_within_days(aae397: str, collect_time: str, days: int = 7) -> bool: """ 判断数据是否在指定天数内 条件:发布日期 AND 采集时间 都在N天内 """ today = datetime.now().date() cutoff_date = today - timedelta(days=days) publish_date = parse_aae397(aae397) collect_date = parse_collect_time(collect_time) return publish_date >= cutoff_date and collect_date >= cutoff_date ``` ### 5.3 RabbitMQ服务模块 #### 消息TTL机制 ```python # 队列声明时设置消息TTL channel.queue_declare( queue='job_data', durable=True, arguments={ 'x-message-ttl': 604800000 # 7天(毫秒) } ) # 发送消息时也设置TTL(双重保障) channel.basic_publish( exchange='', routing_key='job_data', body=message, properties=pika.BasicProperties( delivery_mode=2, # 持久化 expiration='604800000' # 7天 ) ) ``` #### 优势 - 消息级别TTL,精确控制每条消息的过期时间 - 过期消息自动删除,无需手动清理 - 队列中始终保持最近7天的有效数据 ### 5.4 REST API接口 | 接口 | 方法 | 说明 | |------|------|------| | `/consume` | GET | 消费队列数据,支持batch_size参数 | | `/queue/size` | GET | 获取队列消息数量 | | `/status` | GET | 查看采集进度和状态 | | `/tasks` | GET | 获取任务列表 | | `/crawl/start` | POST | 手动触发采集任务 | | `/crawl/stop` | POST | 停止采集任务 | #### 接口详情 **GET /consume** ```json // Request GET /consume?batch_size=10 // Response { "code": 0, "data": [ { "_id": "uuid", "_task_id": "00f3b445-...", "_crawl_time": "2026-01-15T10:30:00", "Std_class": "机动车司机/驾驶", "aca112": "保底1万+五险+港内A2驾驶员", "AAB004": "青岛唐盛物流有限公司", "acb241": "1-1.5万", "aab302": "青岛黄岛区", "aae397": "1月13日", "Collect_time": "2026-01-15", ... } ], "count": 10 } ``` **GET /status** ```json { "code": 0, "data": { "tasks": [ { "task_id": "00f3b445-...", "task_name": "青岛招聘数据", "total": 270000, "last_start_offset": 269900, "status": "completed", "filtered_count": 15000, "produced_count": 15000, "is_running": false } ], "queue_size": 12345, "running_count": 0 } } ``` --- ## 6. 数据模型 ### 6.1 原始数据保留 数据采集后保留原始字段名,仅添加元数据: | 字段 | 说明 | |------|------| | _id | 唯一标识(UUID) | | _task_id | 任务ID | | _crawl_time | 入库时间 | | 其他字段 | 保留原始API返回的所有字段 | ### 6.2 RabbitMQ消息格式 ```json { "_id": "uuid", "_task_id": "00f3b445-d8ec-44e8-88b2-4b971a228b1e", "_crawl_time": "2026-01-15T10:30:00", "Std_class": "机动车司机/驾驶", "aca112": "保底1万+五险+港内A2驾驶员", "AAB004": "青岛唐盛物流有限公司", "AAB019": "民营", "acb241": "1-1.5万", "aab302": "青岛黄岛区", "AAE006": "青岛市黄岛区...", "aae397": "1月13日", "Collect_time": "2026-01-15", "ACE760": "https://www.zhaopin.com/...", "acb22a": "岗位职责...", "Experience": "5-10年", "aac011": "学历不限", "acb240": "1人", "AAB022": "交通/运输/物流", "Num_employers": "20-99人", "AAE004": "张先生/HR", "AAB092": "公司简介..." } ``` --- ## 7. 配置说明 ### 配置文件 `config/config.yml` ```yaml # 应用配置 app: name: job-crawler version: 1.0.0 debug: false # 八爪鱼API配置 api: base_url: https://openapi.bazhuayu.com username: "your_username" password: "your_password" batch_size: 100 # 多任务配置 tasks: - id: "00f3b445-d8ec-44e8-88b2-4b971a228b1e" name: "青岛招聘数据" enabled: true - id: "task-id-2" name: "任务2" enabled: false # RabbitMQ配置 rabbitmq: host: rabbitmq # Docker内部服务名 port: 5672 username: guest password: guest queue: job_data message_ttl: 604800000 # 消息过期时间:7天(毫秒) # 采集配置 crawler: filter_days: 7 # 数据有效期(天) max_expired_batches: 3 # 连续过期批次阈值 max_workers: 5 # 最大并行任务数 auto_start: true # 容器启动时自动开始采集 # 数据库配置 database: path: data/crawl_progress.db ``` --- ## 8. 部署流程 ### 8.1 Docker Compose 一键部署 ```bash # 1. 配置 cd job_crawler cp config/config.yml.docker config/config.yml # 编辑 config/config.yml 填入账号密码 # 2. 构建镜像 ./deploy.sh build # 3. 启动服务 ./deploy.sh up # 4. 查看日志 ./deploy.sh logs # 5. 查看状态 ./deploy.sh status ``` ### 8.2 部署脚本命令 | 命令 | 说明 | |------|------| | `./deploy.sh build` | 构建镜像 | | `./deploy.sh up` | 启动服务 | | `./deploy.sh down` | 停止服务 | | `./deploy.sh restart` | 重启应用 | | `./deploy.sh logs` | 查看应用日志 | | `./deploy.sh status` | 查看服务状态 | | `./deploy.sh reset` | 清理数据卷并重启 | ### 8.3 服务端口 | 服务 | 端口 | 说明 | |------|------|------| | FastAPI | 8000 | HTTP API | | RabbitMQ | 5672 | AMQP协议 | | RabbitMQ | 15672 | 管理界面 | ### 8.4 访问地址 - API文档: http://localhost:8000/docs - RabbitMQ管理界面: http://localhost:15672 (guest/guest) --- ## 9. 数据流向 ``` 八爪鱼API → 采集服务(过滤7天内数据) → RabbitMQ(TTL=7天) → 第三方消费 ↓ 过期自动删除 ``` --- ## 10. Token自动刷新机制 系统实现了Token自动管理: 1. 首次请求时自动获取Token 2. Token缓存在内存中 3. 请求前检查Token有效期(提前5分钟刷新) 4. 遇到401错误自动重新获取Token --- ## 11. 异常处理 | 异常场景 | 处理策略 | |---------|---------| | API请求失败 | 重试3次,指数退避 | | Token过期 | 自动刷新Token | | RabbitMQ连接失败 | 自动重连 | | 日期解析失败 | 记录日志,跳过该条数据 | --- ## 12. 快速启动 ```bash # 1. 配置 cd job_crawler cp config/config.yml.docker config/config.yml # 编辑 config/config.yml 填入账号密码 # 2. 一键启动 ./deploy.sh build ./deploy.sh up # 3. 查看采集日志 ./deploy.sh logs # 4. 消费数据 curl http://localhost:8000/consume?batch_size=10 # 5. 查看队列大小 curl http://localhost:8000/queue/size ```