如何用 scan_method: local_parser 把自己的抓取脚本接入 career-ops,让 scan.mjs 零 token 抓取公司招聘页
【免费下载链接】career-opsOpen-source AI job search: scan job portals, evaluate listings into a structured A-H report with a global 1-5 score, tailor your CV, track applications — runs locally in your AI coding CLI (Claude Code, Codex, OpenCode, Antigravity…)项目地址: https://gitcode.com/GitHub_Trending/ca/career-ops
career-ops 的scan.mjs默认用 Playwright 或 WebSearch 这类会消耗 LLM token 的方式发现职位。如果你的目标公司招聘页是 SSR/静态 HTML、有稳定的结构或文档化的接口,可以自己写一个本地抓取脚本,通过scan_method: local_parser接入扫描流程:解析器作为本地命令运行,把规范化后的职位 JSON 打到 stdout,scan.mjs的发现阶段就0 LLM tokens消耗,只有规范化后的职位行进入后续 title 过滤、去重和 pipeline 输出流程。完整机制见 docs/local-parser-cookbook.md 和 providers/local-parser.mjs。
什么情况下该用 local_parser
按 docs/local-parser-cookbook.md 的说明,满足以下条件时适合:
- 公司招聘页有稳定 HTML、SSR 渲染,或有文档化的 endpoint,本地解析比 Playwright 更容易;
- 脚本语言不限——JavaScript、Python、shell、Go 或机器上任何可执行运行时都行;
career-ops不自带公司专属解析脚本,脚本由你提供,portals.yml指向它。
一个重要的边界:如果某公司已经能被scan.mjs自动识别出 Greenhouse、Ashby 或 Lever 的公开 API,通常不需要写解析器;而 local parser 失败时,scan.mjs会记录失败并对该公司回退到 API 路径,而不是直接丢弃。
准备条件
- 已装好 career-ops 项目并能运行
node scan.mjs(脚本走npm run scan等价)。 - 按 templates/portals.example.yml 顶部说明,先把该模板复制为项目根目录的
portals.yml,再编辑其中的title_filter和tracked_companies。 - 你的抓取脚本必须放在项目目录内(相对路径会相对项目根解析,providers/local-parser.mjs 会拒绝解析到项目根之外的路径)。
第一步:写一个满足 stdout 契约的抓取脚本
解析器必须把下列三种 JSON 形状之一打印到 stdout(文档示例,来自 docs/local-parser-cookbook.md):
[ { "title": "Senior AI Engineer", "url": "https://example.com/jobs/123", "location": "Remote" } ]{ "jobs": [ { "title": "Senior AI Engineer", "url": "https://example.com/jobs/123", "location": "Remote" } ] }{ "results": [ { "title": "Senior AI Engineer", "url": "https://example.com/jobs/123", "location": "Remote" } ] }字段要求:title和url必填;company可选,缺省时扫描器使用tracked_companies条目的name;相对 URL 会按careers_url解析成绝对地址。仓库里 tests/providers/_fixture-local-parser.mjs 是一个可直接参考的示例脚本:它按参数打印上面契约的不同形状,包括{ jobs: [...] }包裹和缺title/url会被丢弃的行。
一个满足契约的最小脚本形态(输出为文档示例数据;真实脚本需要自己请求目标公司页面并解析):
// scripts/parsers/example-company-jobs.js console.log(JSON.stringify([ { "title": "Senior AI Engineer", "url": "https://example.com/jobs/123", "location": "Remote" } ]));运行时还有几条硬约束,来自 providers/local-parser.mjs:
parser.command必须是白名单解释器之一:python3、python、node、deno、bun、sh、bash,或者是项目内的一个文件;不允许任意二进制。- 使用白名单解释器时,脚本必须是解释器的第一个参数(
node --eval、python -c这类内联代码标志会被拒绝)。 - 脚本以项目根为 cwd 执行,且不经过 shell 展开(
execFile),所以args里的{careers_url}和{company}由scan.mjs在执行前做占位符替换;careers_url必须是 http(s),公司名不能以-开头。 - 默认超时 20 秒、stdout 缓冲上限 2 MB,可用
parser.timeout_ms和parser.max_buffer_bytes覆盖。 - stdout 不是合法 JSON 时抛错
local parser returned invalid JSON。
args是可选的:公司专属脚本通常把源 URL、选择器、分页规则直接写死在脚本里,用不到args;只有想让一个脚本复用到多家公司时才通过args传{careers_url}/{company}之类的参数。
第二步:在 portals.yml 中注册解析器
在portals.yml的tracked_companies:下为该公司添加一条目(下面为文档示例值,name、careers_url、脚本路径需替换成你自己的公司):
- name: Example Company careers_url: https://example.com/careers scan_method: local_parser parser: command: node script: scripts/parsers/example-company-jobs.js format: jobs-json-v1 enabled: truePython 脚本则把command换成python3、script换成.py路径(见 templates/portals.example.yml 中注释掉的 JS/Python 示例)。路由规则:parser.command+parser.script都设置时,local-parser优先于其他 provider 运行,即先本地解析,再谈 ATS API 回退。
第三步:用 validate:portals 离线校验配置
先跑离线校验器再跑扫描。它不请求任何职位板,只检查 YAML 形状、未知 provider、URL 格式和 local parser 块是否合法(见 docs/SCRIPTS.md 的validate:portals一节):
npm run validate:portals退出码0表示没有错误(允许有 warning),1表示发现错误。如果配置有问题,应在这一步就被拦下来,而不是等到扫描时才报 parser 错误。
第四步:运行扫描并核对结果
npm run scan # 等价于 node scan.mjsscan.mjs读取portals.yml中的目标公司,把命中的职位输出到 stdout,并可选追加到data/pipeline.md(见 docs/SCRIPTS.md 的scan一节)。退出码:0扫描完成,1配置错误或找不到portals.yml。
判断接入是否成功,按顺序看三点:
validate:portals退出码为0;scan.mjs退出码为0,且 stdout 出现目标公司的职位行;- 运行汇总会统计本地解析器覆盖的公司数(
scan.mjs汇总中的N local parser),有职位行且计数大于 0,说明该公司走的是本地脚本而不是 Playwright/API 通道。
如果解析器同时会写完整 JSON 快照用于调试或审计,放到data/parser-output/{company}/下,并把生成的 JSON 排除在 git 之外(.gitkeep占位文件是唯一允许提交的例外)。
失败与回退行为
- 本地 parser 失败:若该公司同时存在可探测的 Greenhouse、Ashby 或 Lever API 源,
scan.mjs记录解析器失败后回退到 API 路径,公司不会从扫描中消失;此时运行信息里会带local parser failed, used API fallback说明。 - 脚本路径或命令不安全:
detect()阶段解析不了调用(未知命令、脚本不在项目内、缺失脚本、内联代码标志)时,该条目直接跳过,报错信息如local-parser: path escapes the project root、local-parser: the parser script must be the interpreter's first argument。 - 在 agent 扫描模式下:按 modes/scan.md 的规则,Level 0(local parser)成功的公司进入
local_parser_ok集合后,agent 必须跳过该公司的 Playwright(Level 1)和 API(Level 2),Level 3 的 WebSearch 通用查询仍保留但过滤掉该公司命中;也不应为已有 local parser 的公司另建专用search_queries。这就是 local_parser 省 token 的实际效果——防止模型重复抓取同一公司。
脚本写好后,接入就是一条固定链路:脚本进项目目录 →portals.yml挂parser块 →validate:portals通过 →npm run scan以 0 token 完成该公司的发现。后续公司招聘页结构变化时,只需改脚本本身,portals.yml与扫描流程不用动。
【免费下载链接】career-opsOpen-source AI job search: scan job portals, evaluate listings into a structured A-H report with a global 1-5 score, tailor your CV, track applications — runs locally in your AI coding CLI (Claude Code, Codex, OpenCode, Antigravity…)项目地址: https://gitcode.com/GitHub_Trending/ca/career-ops
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考