jobhound-lite:把一个越写越重的数据采集工具做减法

之前想分析招聘市场的岗位数据分布,顺手写了个工具从几个主流招聘平台采集公开岗位数据。工具一开始越写越重——加了数据库、ORM、迁移、API 层……

某天想给朋友用,发现一个尴尬的问题:他只想输入城市和关键词、拿一份 Excel,却得先装数据库、跑迁移、理解一整套 schema。太重了。

于是我剥了一层皮,做了 jobhound-lite:没有数据库,没有 API,就一个命令行——装依赖、登录、采集导出,三步出结果。这篇文章记一下设计思路。

一、为什么不要数据库

完整版用数据库,是因为我需要增量:今天采一遍、明天再采一遍,看新增岗位、记录薪资变化。这要求持久化和去重指针。

但给别人用的场景完全不同——别人可能就跑一次,看看某个城市的岗位分布。这种场景下,数据库是负担,每次还得解释”数据在哪””怎么清空”。

所以 lite 版的核心决定:结果只活在内存里,跑完直接写 JSON 或 Excel 文件,不落地任何持久存储。代价是失去增量能力,但对一次性使用完全够用。

二、技术栈与多平台适配

整体用 Python 3.11+,依赖压到 5 个:DrissionPage(浏览器自动化)、typer(CLI)、rich(终端输出)、openpyxl(Excel)、selectolax(HTML 解析)。

浏览器层用 DrissionPage 复用系统 Chrome,不单独装 Chromium。几个平台的页面结构各不相同,适配策略也各异,这是工具最核心的部分:

  • 有的平台列表页每个岗位卡片里就内嵌了包含薪资/经验/学历等字段的 JSON,光列表页就能拿全结构化数据,不用进详情页。
  • 有的平台列表页卡片里同样带薪资、公司、城市、经验、学历、行业等字段,在解析层提取出来即可。
  • 有的平台列表页只有标题和链接,薪资和岗位描述(JD)都在详情页,需要逐个访问详情页解析。

也就是说,前两类走”列表直出”,第三类走”列表 + 详情页”。这种差异决定了各平台的数据完整度不同:走详情页的那个 JD 最全,列表直出的次之,有个别平台详情页访问受限,干脆不要 JD 正文。

三、去重与薪资解析

几个平台经常有同一个岗位重复发版——同一家公司的”前端开发”在多个平台都有,甚至同平台发两次。lite 版做了两层去重:

  1. 同平台:岗位 ID 相同的只留一个
  2. 跨平台:归一化后的”公司 + 标题 + 城市”相同的,按平台数据完整度排优先级,留数据最全的那条

标题归一化要去括号后缀——前端开发工程师(WZ)Java 开发(J10230) 这种工号后缀去掉后才能比对,不然同一个岗会因为标题带不带工号被当成两个。

薪资解析支持各平台的常见格式:15-25k1.5-2万8000-10000元/月15-25k·14薪。统一转成”千元/月”的整数上下限,方便后续筛选。过滤时用”触及语义”——15-20k 的岗位用 18 过滤会命中(区间触及 18k),而不是只有起薪 ≥ 18 才命中,这更符合”看这个岗位能不能给到 18k”的直觉。

四、限制

工具能跑,但不是万能的:

  • 只支持 21 个一二线城市。每个平台对城市有内部数字码,得手动映射,目前录了 21 个(上海/北京/深圳/广州/杭州/成都/南京/武汉/苏州/西安/重庆/天津/长沙/青岛/郑州/厦门/福州/济南/宁波/无锡/大连)。
  • 部分岗位的数值化薪资解析失败。原文保留,可人工补。
  • 个别平台没有行业/公司规模字段,结构差异所致。
  • 个别平台没有 JD 正文,详情页访问受限时的取舍。
  • 必须用有窗口的模式跑。采集时会弹 Chrome 窗口,不能完全后台。
  • 只适合本地手动跑,不要放自动化流程里。

五、怎么用

三步:

1
2
3
4
cd lite
uv sync # 装依赖
uv run jobhound-lite login <platform> # 登录(每平台一次)
uv run jobhound-lite crawl <platform> --city 成都 --keyword 前端 --pages 5 -o jobs.json

输出格式按文件后缀自动判断:.json 出 JSON,.xlsx 出 Excel。传 all 作为平台名会多平台串行采集 + 跨平台去重,一次出全量:

1
uv run jobhound-lite crawl all --city 成都 --keyword Java --pages 5 -o java.xlsx

输出的 Excel 字段:平台 / 岗位标题 / 公司 / 城市 / 区 / 薪资原文 / 薪资下限 / 薪资上限 / 年薪倍数 / 经验 / 学历 / 行业 / 公司规模 / JD / 详情链接。

写在最后

这个 lite 版剥皮的过程,让我对”自用工具”和”给别人用的工具”的区别有了更深的体会。自用可以堆功能,给别人用得做减法——把”我需要但别人不需要”的东西全砍掉。砍到最后只剩 1400 行代码、12 个文件,几个平台都能跑通,装一个依赖就能用。

工具先能用起来,比工具完美更重要。