Files
tg_crawl/README.md
2026-02-26 20:00:06 +08:00

292 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# jobs_robots
本项目用于采集 Telegram 招聘数据并进行结构化清洗,当前统一使用 MySQL。
## 1. 当前流程
1. `main.py`
-`config.json` 读取数据源、时间窗口、限频、MySQL 配置。
- 爬取 Telegram 消息,写入 MySQL `messages`
- 维护每个来源的增量游标到 `sync_state`
2. `clean_to_structured.py`
- 从 MySQL `messages` 增量读取新增消息(按 `messages.id` + `clean_state` 检查点)。
- 按来源规则清洗(`@DeJob_official` 有专用规则,其他走通用规则)。
- 仅保留招聘类数据,写入 MySQL `structured_jobs`
3. `run_daily_incremental.sh`
- 每日调度入口。
- 运行前自动更新 `config.json` 时间窗口(滚动窗口)。
- 依次执行 `main.py``clean_to_structured.py`
## 2. 数据库表
### 2.1 原始层
- `messages`
- 原始消息存储(`source + message_id` 唯一)
- `sync_state`
- Telegram 增量抓取游标
### 2.2 清洗层
- `structured_jobs`
- 结构化岗位数据(`source + message_id` 唯一)
- `clean_state`
- 清洗增量检查点(`pipeline_name -> last_message_row_id`
## 2.3 字段级数据字典(详细)
### messagesTelegram 原始消息)
- `id`BIGINT, PK, 自增)
含义MySQL 行主键,清洗增量检查点使用这个字段。
示例:`530812`
- `source`VARCHAR
含义:消息来源标识(频道/群组),通常是 `@xxx`
示例:`@DeJob_official`
- `chat_id`BIGINT, 可空)
含义Telegram 实体 ID。
示例:`-1001234567890`
- `message_id`BIGINT
含义:该 source 内部的消息 ID。
约束:与 `source` 组成唯一键。
- `content`LONGTEXT, 可空)
含义:抓取到的消息正文(含非文本补充段,如 `MEDIA_JSON`)。
示例:招聘 markdown 文本 + `[MEDIA_TYPE] ...`
- `date`DATETIME
含义消息时间UTC
示例:`2026-02-26 09:31:10`
- `created_at`DATETIME
含义:该条记录写入数据库时间。
### sync_state抓取增量状态
- `source`VARCHAR, PK
含义:来源标识,与 `messages.source` 对应。
- `last_message_id`BIGINT
含义:该来源已抓取到的最大 message_id。
用途:下次抓取时只拉 `message_id > last_message_id`
- `updated_at`DATETIME
含义:该来源游标最近更新时间。
### structured_jobs清洗后结构化岗位
- `id`BIGINT, PK, 自增)
含义:结构化表主键。
- `source`VARCHAR
含义:来源标识。
示例:`@DeJob_official`
- `source_channel`VARCHAR, 可空)
含义:来源品牌/渠道归类。
示例:`DeJob`
- `parser_name`VARCHAR
含义:使用的解析器名称。
示例:`dejob_official` / `generic`
- `parser_version`VARCHAR
含义:解析器版本号,用于规则演进追踪。
示例:`v1`
- `chat_id`BIGINT, 可空)
含义:原始 Telegram chat_id。
- `message_id`BIGINT
含义:原始消息 IDsource 内)。
约束:与 `source` 组成唯一键。
- `message_date`DATETIME
含义原始消息时间UTC
- `job_type`VARCHAR, 可空)
含义:岗位类型标记。当前仅保留 `招聘`
示例:`招聘`
- `company_name`VARCHAR, 可空)
含义:公司/项目方名称。
示例:`88EX`
- `industry_tags_json`JSON
含义:行业/赛道标签数组。
示例:`[\"CEX\",\"Infra\"]`
- `company_intro`LONGTEXT, 可空)
含义:公司简介文本。
- `company_url`TEXT, 可空)
含义:公司官网/介绍页链接。
- `work_mode`VARCHAR
含义:办公模式。
枚举:`remote | onsite | hybrid | unknown`
- `job_nature`VARCHAR
含义:用工性质。
枚举:`full_time | part_time | contract | intern | freelance | unknown`
- `job_location_text`VARCHAR, 可空)
含义:主地点文本(首个地点)。
- `job_location_tags_json`JSON, 可空)
含义:地点标签数组。无地点时为 `NULL`(不是空数组)。
- `employment_type_raw`TEXT, 可空)
含义:原始“合作方式”文本,便于回溯规则。
示例:`🛵 合作方式:#全职 #远程 #吉隆坡`
- `position_name`VARCHAR, 可空)
含义:岗位主名称。
示例:`社区运营`
- `position_tags_json`JSON
含义:岗位标签数组。
示例:`[\"社区运营\",\"运营\"]`
- `salary_raw`TEXT, 可空)
含义:薪资原始字符串。
示例:`$1000 - $3000 / month`
- `salary_currency`VARCHAR, 可空)
含义:薪资币种(已识别)。
示例:`USD`
- `salary_min`BIGINT, 可空)
含义:薪资下限数值。
- `salary_max`BIGINT, 可空)
含义:薪资上限数值。
- `salary_period`VARCHAR, 可空)
含义:薪资周期。
枚举:`month | year | day | NULL`
- `responsibilities_json`JSON
含义:岗位职责数组(按条目拆分)。
- `requirements_json`JSON
含义:岗位要求数组(按条目拆分)。
- `apply_email`VARCHAR, 可空)
含义:投递邮箱。
- `apply_telegram`VARCHAR, 可空)
含义:投递 Telegram 用户名。
示例:`@lulu_lucky1`
- `job_source_url`TEXT, 可空)
含义:岗位来源原文链接(如 DeJob 详情页)。
- `body_text`LONGTEXT
含义:清洗后的主体文本(去除部分技术元段)。
- `raw_content`LONGTEXT
含义:原始消息内容快照(用于审计/回刷)。
- `cleaned_at`DATETIME
含义:最近清洗/更新该条结构化记录的时间。
### clean_state清洗增量状态
- `pipeline_name`VARCHAR, PK
含义:清洗流程标识。
示例:`structured_cleaner_v1`
- `last_message_row_id`BIGINT
含义:已处理到的 `messages.id` 最大值。
用途:下次清洗只处理更大的 `messages.id`
- `updated_at`DATETIME
含义:检查点更新时间。
## 3. 配置文件说明config.json
关键字段:
- `sources`: Telegram 来源列表
- `time_window.enabled`: 是否启用时间窗口
- `time_window.start` / `time_window.end`: 抓取窗口(脚本会每日自动刷新)
- `daily_window_days`: 滚动窗口天数(当前默认 `2`
- `throttle`: 限频配置
- `enabled`
- `per_message_delay_sec`
- `between_sources_delay_sec`
- `mysql`: MySQL 连接配置
- `host`, `port`, `user`, `password`, `database`, `charset`
## 4. 运行方式
### 4.1 手动运行
```bash
uv run main.py
uv run clean_to_structured.py
```
### 4.2 每日定时(推荐)
脚本:`run_daily_incremental.sh`
示例 crontab每天 01:10
```cron
10 1 * * * /home/liam/code/python/jobs_robots/run_daily_incremental.sh
```
日志文件:
- `logs/app.log`
- `logs/clean_to_structured.log`
- `logs/daily_job.log`
## 5. 增量策略
### Telegram 抓取增量
- 依据 `sync_state.last_message_id`
- 每个来源独立增量
### 清洗增量
- 依据 `clean_state.last_message_row_id`
- 每次只处理 `messages.id > checkpoint`
- 成功后更新 checkpoint
## 6. 字段约定(结构化就业类型)
`structured_jobs` 使用拆分字段,不再依赖 `employment_type_json`
- `work_mode`: `remote | onsite | hybrid | unknown`
- `job_nature`: `full_time | part_time | contract | intern | freelance | unknown`
- `job_location_text`: 主地点文本
- `job_location_tags_json`: 地点数组(无地点为 `NULL`
- `employment_type_raw`: 原始“合作方式”行
## 7. 常见问题
1. 为什么当天凌晨跑出来窗口看着不对?
- 当前滚动窗口按 UTC 日期更新。如果需要按本地时区(如 Asia/Shanghai可再改。
2. 为什么清洗没有新增?
-`clean_state` 检查点是否已经到最新。
-`messages` 是否有新数据。
3. 为什么 MySQL 报字段超长/类型错误?
- 优先看对应脚本日志,字段已做多数保护;若仍报错,保留错误堆栈并反馈。
## 8. 协作建议
- 改规则时优先只改对应来源 parser避免影响全局。
- 改字段前先确认 `structured_jobs` 兼容性与迁移策略。
- 所有定时行为以 `run_daily_incremental.sh` 为统一入口,避免多处调度冲突。