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

8.2 KiB
Raw Blame History

jobs_robots

本项目用于采集 Telegram 招聘数据并进行结构化清洗,当前统一使用 MySQL。

1. 当前流程

  1. main.py
  • config.json 读取数据源、时间窗口、限频、MySQL 配置。
  • 爬取 Telegram 消息,写入 MySQL messages
  • 维护每个来源的增量游标到 sync_state
  1. clean_to_structured.py
  • 从 MySQL messages 增量读取新增消息(按 messages.id + clean_state 检查点)。
  • 按来源规则清洗(@DeJob_official 有专用规则,其他走通用规则)。
  • 仅保留招聘类数据,写入 MySQL structured_jobs
  1. run_daily_incremental.sh
  • 每日调度入口。
  • 运行前自动更新 config.json 时间窗口(滚动窗口)。
  • 依次执行 main.pyclean_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 原始消息)

  • idBIGINT, PK, 自增)
    含义MySQL 行主键,清洗增量检查点使用这个字段。
    示例:530812

  • sourceVARCHAR
    含义:消息来源标识(频道/群组),通常是 @xxx
    示例:@DeJob_official

  • chat_idBIGINT, 可空)
    含义Telegram 实体 ID。
    示例:-1001234567890

  • message_idBIGINT
    含义:该 source 内部的消息 ID。
    约束:与 source 组成唯一键。

  • contentLONGTEXT, 可空)
    含义:抓取到的消息正文(含非文本补充段,如 MEDIA_JSON)。
    示例:招聘 markdown 文本 + [MEDIA_TYPE] ...

  • dateDATETIME
    含义消息时间UTC
    示例:2026-02-26 09:31:10

  • created_atDATETIME
    含义:该条记录写入数据库时间。

sync_state抓取增量状态

  • sourceVARCHAR, PK
    含义:来源标识,与 messages.source 对应。

  • last_message_idBIGINT
    含义:该来源已抓取到的最大 message_id。
    用途:下次抓取时只拉 message_id > last_message_id

  • updated_atDATETIME
    含义:该来源游标最近更新时间。

structured_jobs清洗后结构化岗位

  • idBIGINT, PK, 自增)
    含义:结构化表主键。

  • sourceVARCHAR
    含义:来源标识。
    示例:@DeJob_official

  • source_channelVARCHAR, 可空)
    含义:来源品牌/渠道归类。
    示例:DeJob

  • parser_nameVARCHAR
    含义:使用的解析器名称。
    示例:dejob_official / generic

  • parser_versionVARCHAR
    含义:解析器版本号,用于规则演进追踪。
    示例:v1

  • chat_idBIGINT, 可空)
    含义:原始 Telegram chat_id。

  • message_idBIGINT
    含义:原始消息 IDsource 内)。
    约束:与 source 组成唯一键。

  • message_dateDATETIME
    含义原始消息时间UTC

  • job_typeVARCHAR, 可空)
    含义:岗位类型标记。当前仅保留 招聘
    示例:招聘

  • company_nameVARCHAR, 可空)
    含义:公司/项目方名称。
    示例:88EX

  • industry_tags_jsonJSON
    含义:行业/赛道标签数组。
    示例:[\"CEX\",\"Infra\"]

  • company_introLONGTEXT, 可空)
    含义:公司简介文本。

  • company_urlTEXT, 可空)
    含义:公司官网/介绍页链接。

  • work_modeVARCHAR
    含义:办公模式。
    枚举:remote | onsite | hybrid | unknown

  • job_natureVARCHAR
    含义:用工性质。
    枚举:full_time | part_time | contract | intern | freelance | unknown

  • job_location_textVARCHAR, 可空)
    含义:主地点文本(首个地点)。

  • job_location_tags_jsonJSON, 可空)
    含义:地点标签数组。无地点时为 NULL(不是空数组)。

  • employment_type_rawTEXT, 可空)
    含义:原始“合作方式”文本,便于回溯规则。
    示例:🛵 合作方式:#全职 #远程 #吉隆坡

  • position_nameVARCHAR, 可空)
    含义:岗位主名称。
    示例:社区运营

  • position_tags_jsonJSON
    含义:岗位标签数组。
    示例:[\"社区运营\",\"运营\"]

  • salary_rawTEXT, 可空)
    含义:薪资原始字符串。
    示例:$1000 - $3000 / month

  • salary_currencyVARCHAR, 可空)
    含义:薪资币种(已识别)。
    示例:USD

  • salary_minBIGINT, 可空)
    含义:薪资下限数值。

  • salary_maxBIGINT, 可空)
    含义:薪资上限数值。

  • salary_periodVARCHAR, 可空)
    含义:薪资周期。
    枚举:month | year | day | NULL

  • responsibilities_jsonJSON
    含义:岗位职责数组(按条目拆分)。

  • requirements_jsonJSON
    含义:岗位要求数组(按条目拆分)。

  • apply_emailVARCHAR, 可空)
    含义:投递邮箱。

  • apply_telegramVARCHAR, 可空)
    含义:投递 Telegram 用户名。
    示例:@lulu_lucky1

  • job_source_urlTEXT, 可空)
    含义:岗位来源原文链接(如 DeJob 详情页)。

  • body_textLONGTEXT
    含义:清洗后的主体文本(去除部分技术元段)。

  • raw_contentLONGTEXT
    含义:原始消息内容快照(用于审计/回刷)。

  • cleaned_atDATETIME
    含义:最近清洗/更新该条结构化记录的时间。

clean_state清洗增量状态

  • pipeline_nameVARCHAR, PK
    含义:清洗流程标识。
    示例:structured_cleaner_v1

  • last_message_row_idBIGINT
    含义:已处理到的 messages.id 最大值。
    用途:下次清洗只处理更大的 messages.id

  • updated_atDATETIME
    含义:检查点更新时间。

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 手动运行

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.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可再改。
  1. 为什么清洗没有新增?
  • clean_state 检查点是否已经到最新。
  • messages 是否有新数据。
  1. 为什么 MySQL 报字段超长/类型错误?
  • 优先看对应脚本日志,字段已做多数保护;若仍报错,保留错误堆栈并反馈。

8. 协作建议

  • 改规则时优先只改对应来源 parser避免影响全局。
  • 改字段前先确认 structured_jobs 兼容性与迁移策略。
  • 所有定时行为以 run_daily_incremental.sh 为统一入口,避免多处调度冲突。