8.2 KiB
jobs_robots
本项目用于采集 Telegram 招聘数据并进行结构化清洗,当前统一使用 MySQL。
1. 当前流程
main.py
- 从
config.json读取数据源、时间窗口、限频、MySQL 配置。 - 爬取 Telegram 消息,写入 MySQL
messages。 - 维护每个来源的增量游标到
sync_state。
clean_to_structured.py
- 从 MySQL
messages增量读取新增消息(按messages.id+clean_state检查点)。 - 按来源规则清洗(
@DeJob_official有专用规则,其他走通用规则)。 - 仅保留招聘类数据,写入 MySQL
structured_jobs。
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 字段级数据字典(详细)
messages(Telegram 原始消息)
-
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)
含义:原始消息 ID(source 内)。
约束:与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: 限频配置enabledper_message_delay_secbetween_sources_delay_sec
mysql: MySQL 连接配置host,port,user,password,database,charset
4. 运行方式
4.1 手动运行
uv run main.py
uv run clean_to_structured.py
4.2 每日定时(推荐)
脚本:run_daily_incremental.sh
示例 crontab(每天 01:10):
10 1 * * * /home/liam/code/python/jobs_robots/run_daily_incremental.sh
日志文件:
logs/app.loglogs/clean_to_structured.loglogs/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 | unknownjob_nature:full_time | part_time | contract | intern | freelance | unknownjob_location_text: 主地点文本job_location_tags_json: 地点数组(无地点为NULL)employment_type_raw: 原始“合作方式”行
7. 常见问题
- 为什么当天凌晨跑出来窗口看着不对?
- 当前滚动窗口按 UTC 日期更新。如果需要按本地时区(如 Asia/Shanghai)可再改。
- 为什么清洗没有新增?
- 看
clean_state检查点是否已经到最新。 - 看
messages是否有新数据。
- 为什么 MySQL 报字段超长/类型错误?
- 优先看对应脚本日志,字段已做多数保护;若仍报错,保留错误堆栈并反馈。
8. 协作建议
- 改规则时优先只改对应来源 parser,避免影响全局。
- 改字段前先确认
structured_jobs兼容性与迁移策略。 - 所有定时行为以
run_daily_incremental.sh为统一入口,避免多处调度冲突。