news 2026/9/30 11:15:05

在 ai-job-search 中使用 linkedin-cli:基于 LinkedIn 公开职位数据的免认证零依赖求职搜索 CLI

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在 ai-job-search 中使用 linkedin-cli:基于 LinkedIn 公开职位数据的免认证零依赖求职搜索 CLI
  • AI 应用
  • AI 技能

【免费下载链接】ai-job-search

The job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.

项目地址:https://gitcode.com/GitHub_Trending/ai/ai-job-search
点击查看免费下载

导读

linkedin-cli 是 ai-job-search 仓库中linkedin-search技能附带的命令行工具:它直接调用 LinkedIn 公开的jobs-guest匿名接口,让你可以在任意国家/地区(含全球 Remote 职位)、任意行业下搜索职位列表,并拉取单个职位的完整详情。它无需任何认证、无需 API Key,且零运行时依赖——只要装有bun,克隆仓库即可直接运行。读完本文,你将掌握它的安装方式、search与detail两条命令的全部参数用法、三种输出格式、JSON 错误协议,以及从 URL 构造到 HTML 解析、指数退避重试的完整底层实现原理。

一、工具定位:一个免安装、免认证的求职搜索 CLI

linkedin-cli的核心设计目标可以概括为三点:

  • 免认证:数据源是 LinkedIn 的公开jobs-guest端点(seeMoreJobPostings/search与jobPosting/<id>),不需要登录态、Cookie 或 API Key;
  • 零依赖:使用纯bun+ 内置fetch实现,没有任何运行时依赖,bun install是可选的,只用于安装 TypeScript 开发类型定义;
  • 全球化:jobs-guest端点对所有市场都是同一套,CLI 的 HTML 解析与具体国家无关,只需通过--location传入不同的地点字符串即可切换市场,同一套代码开箱即用地适用于任何地区的求职者。

正如 SKILL.md 中所注明的,它是仓库"职位门户技能模式"(job-portal-skill pattern)的一个国家无关(country-agnostic)的落地示例,而整个技能与 CLI 的触发入口在仓库中由allowed-tools限定为Bash(bun run .agents/skills/linkedin-search/cli/src/cli.ts *)。

⚠️ 个人使用声明:该工具读取 LinkedIn 的公开职位页面,自动化访问违反 LinkedIn 服务条款(Terms of Service)。请保持低频率访问、不要用于商业用途或批量数据采集,并自行承担运行责任。这一限制贯穿 CLI README、SKILL.md 与 url-reference.md 三处文档。

二、安装与运行前提

由于没有运行时依赖,安装步骤极其简单:

cd .agents/skills/linkedin-search/cli bun install # 可选——仅安装 TypeScript 开发类型定义

即使跳过bun install,CLI 也可以直接运行。package.json 明确声明dependencies为空对象,devDependencies中仅有typescript ^5.4.0与@types/bun 1.3.14;main指向src/cli.ts,并提供了linkedin-search的bin入口,同时预置了三条脚本:

Script命令用途
startbun run src/cli.ts直接启动 CLI
testbun test --timeout 30000运行测试套件(30 秒超时)
typechecktsc --noEmit类型检查

运行时唯一硬性要求是安装 bun(仓库环境的运行时),并具备网络访问 LinkedIn 的能力。

三、命令总览与参数速查

CLI 提供两条命令,入口文件为 src/cli.ts:

命令说明输出格式
search搜索职位列表(--location必填)--format json\|table\|plain(默认json)
detail获取单个职位的完整详情--format json\|plain

所有错误统一写入stderr,格式为{ "error": "...", "code": "..." },进程退出码为1。

3.1search完整参数表

Flag别名说明
--location-l必填。地点字符串,例如"Mumbai, Maharashtra, India"、"Berlin, Germany"、"London, United Kingdom"、"Remote"。
--query-q关键词(职位 / 技能 / 角色),推荐使用。
--jobage发布于最近 N 天内的职位,取值为1、7、14、30;省略则返回全部。
--jobage-minutes发布于最近 N 分钟内的职位(亚天级精度,如30)。与--jobage冲突,二者只能传一个。
--remote工作场所类型过滤:remote|hybrid|onsite。
--page页码(从 1 开始,每页固定 10 条结果)。
--limit-n客户端侧截断,限制最终输出的结果条数。
--formatjson|table|plain,默认json。

3.2detail命令的输入与输出

detail命令接收一个<id|url>参数,随后用--format json|plain控制输出(默认json)。id取自search结果的数字型职位 ID(例如4426311357)。从 SKILL.md 可知,除裸数字 ID 外,它还可以接受:

  • 完整的 LinkedInjobs/view/...URL;
  • urn:li:jobPosting:...URN。

返回内容包含:完整职位描述(description)、资历级别(seniority)、雇佣类型(employment type)、职位职能(job function)与所属行业(industries),以及职位是否仍然开放的状态。

四、快速示例(可直接复制运行)

以下示例全部来自 CLI README 与 SKILL.md,可直接在仓库根目录执行:

# 海得拉巴的软件工程师职位,最近 7 天发布 bun run .agents/skills/linkedin-search/cli/src/cli.ts search -q "backend engineer" -l "Hyderabad, Telangana, India" --jobage 7 --format table # 伦敦的产品设计师职位 bun run .agents/skills/linkedin-search/cli/src/cli.ts search -q "product designer" -l "London, United Kingdom" --format table # 全远程的文档工程师职位 bun run .agents/skills/linkedin-search/cli/src/cli.ts search -q "technical writer" -l "Remote" --remote remote --format table # 班加罗尔的数据工程师职位,最近 30 天发布 bun run .agents/skills/linkedin-search/cli/src/cli.ts search -q "data engineer" -l "Bengaluru, Karnataka, India" --jobage 30 --format table # 柏林的产品经理职位,限远程 bun run .agents/skills/linkedin-search/cli/src/cli.ts search -q "product manager" -l "Berlin, Germany" --remote remote --format table # 全球远程的律师助理职位 bun run .agents/skills/linkedin-search/cli/src/cli.ts search -q "paralegal" -l "Remote" --format table # 最近 30 分钟内发布的远程工程师职位(亚天级时间窗) bun run .agents/skills/linkedin-search/cli/src/cli.ts search -q "engineer" -l "Remote" --jobage-minutes 30 --format table # 获取单个职位的完整详情 bun run .agents/skills/linkedin-search/cli/src/cli.ts detail 4426311357 --format plain

在仓库的 Agent 工作流中,这些命令通常经由linkedin-search技能触发(触发短语包括 "find a job"、"job search"、"remote jobs"、"are there any X jobs in " 等,见 SKILL.md),把搜索结果中的职位 ID 直接交给detail获取完整描述,进而与仓库中的职位评估、简历定制等流程衔接。

五、输出格式详解

格式最佳用途
json默认格式,适合程序化消费,可把返回结果中的 ID 直接传给detail
table快速人眼扫描的紧凑表格
plain阅读单个职位的完整详情(配合detail命令)

search --format json的输出结构(见 src/commands/search.ts)为:

{ "meta": { "count": 10, "page": 1 }, "results": [ { "id": "4426311357", "title": "Backend Engineer", "company": "Example Corp", "companyUrl": "https://www.linkedin.com/company/example", "location": "Hyderabad, Telangana, India", "date": "2026-09-22", "url": "https://www.linkedin.com/jobs/view/4426311357" } ] }

其中每个结果对应一条职位卡片,字段来自 helpers.ts 中定义的JobCard接口。table格式会输出 ID / TITLE / COMPANY / LOCATION / DATE 五列的对齐表格;plain格式则把每个职位压缩为「标题 / 公司 · 地点 · 日期 / id / url」几行。detail --format plain会输出标题、公司、地点、资历、雇佣类型、职能、行业、状态(ACTIVE或CLOSED / EXPIRED)、完整描述与 URL 的易读文本。

六、底层实现原理:从 URL 构造到 HTML 解析

6.1 数据源与请求 URL

两个端点在 helpers.ts 中硬编码:

  • 搜索:https://www.linkedin.com/jobs-guest/jobs/api/seeMoreJobPostings/search
  • 详情:https://www.linkedin.com/jobs-guest/jobs/api/jobPosting

url-reference.md 对这两个端点给出了完整的参数对照:

参数含义示例
keywords自由文本查询data engineer
location地点字符串Mumbai, Maharashtra, India·Berlin, Germany·Remote
f_TPR发布时间窗口(秒)r604800(7 天)、r2592000(30 天)
f_WT工作场所类型1现场 ·2远程 ·3混合
start分页偏移(每页 10 条)0、10、20、…

搜索 URL 的组装逻辑在 src/commands/search.ts 的buildUrl中:--query映射为keywords,--location映射为location,时间窗通过minutesToTPR/jobageToTPR换算成f_TPR秒值,--remote经workTypeFlag映射为f_WT,start = (page - 1) * 10。两个换算函数在 helpers.ts 中实现:

  • jobageToTPR(days):days <= 0或>= 9999时返回null(表示不设置时间窗),否则返回r${days * 86400},例如 7 天 →r604800、30 天 →r2592000;
  • minutesToTPR(minutes):返回r${minutes * 60},例如 30 分钟 →r1800(该行为有测试用例直接断言,见 search.test.ts);
  • workTypeFlag(mode):remote→2、hybrid→3、onsite/on-site→1,未知值返回null。

6.2 HTML 解析策略:浅层标记 + 正则

两个端点返回的都是 HTML 而非 JSON。开发者有意不引入 DOM 解析器:注释中说明 LinkedIn 的卡片标记浅且稳定,而node-html-parser在 LinkedIn 卡片上存在已知的嵌套 bug(helpers.ts)。因此:

  • 搜索页解析(parseJobCards):响应是扁平的一串<li>职位卡片,按data-entity-urn="urn:li:jobPosting:切分成独立块逐块解析,单张损坏的卡片不会拖垮其余结果;每块提取 ID、base-card__full-link中的 URL、base-search-card__title(或sr-onlyspan)中的标题、base-search-card__subtitle中的公司与公司主页、job-search-card__location中的地点、job-search-card__listdate中的datetime属性(helpers.ts);
  • 详情页解析(parseJobDetail):提取top-card-layout__title/topcard__title标题、topcard__org-name-link公司、topcard__flavor--bullet地点,用extractDivContent以标签深度计数的方式(正确处理嵌套<div>)抓取show-more-less-html__markup或description__text富文本描述块,并把<br>、</p>等标签替换为换行保留段落结构;职位标准项(资历、雇佣类型、职能、行业)通过description__job-criteria-subheader标签名与description__job-criteria-text取值的正则成对抓取(helpers.ts);
  • 状态检测:仅限顶卡范围内检查closed-job__flavor或 "no longer accepting applications" 文本;实现注释特别强调,该检测只认"是否出现关闭横幅",isActive: true仅表示"未发现关闭横幅",不代表职位必然开放(标记漂移或同意墙响应同样不会渲染横幅)。

6.3 请求健壮性:超时、限流与指数退避

htmlFetch(helpers.ts)是全部请求的统一出口,具备以下行为:

  • 设置自定义User-Agent(Mozilla/5.0 (compatible; linkedin-search-cli/1.0))、Accept、Accept-Language与X-Requested-With: XMLHttpRequest头;
  • 15 秒请求超时(AbortSignal.timeout(15000));
  • 对429 / 5xx做最多 6 次重试:初始延迟 500ms、指数翻倍至上限 8000ms,并附加 0–500ms 随机抖动(jitter)打散重试时间点;
  • 404 返回空字符串(上层据此判定NOT_FOUND),其他非 2xx 状态直接抛出错误。

6.4detail的 ID 归一化与安全防线

detail并不会盲目接受任何输入。normalizeId(src/commands/detail.ts)依次尝试:

  1. urn:li:jobPosting:<digits>URN 匹配;
  2. 纯 6 位以上数字串;
  3. URL 形式(带或不带 scheme 均可)——仅接受主机名落在linkedin.com域内的jobs/view/<id>路径;
  4. 无 scheme 无斜杠的标题 slug(如software-engineer-1234567890)。

代码注释揭示了一个真实安全教训:旧实现会从任何 URL中取第一个 6 位以上数字段,导致 Greenhouse 或 Lever 的投递链接(职位页自己派发的申请外链)被误解析,从而拉取到恰好同号的无关 LinkedIn 职位并成功退出;如今通过真实的 URL 解析同时拦截了形似域名(linkedin.com.evil.io)与 userinfo 注入(linkedin.com@evil.io)等手段。

七、严格参数校验:宁可报错,不可静默放行

src/cli.ts 实现了两层强校验,并有对应的测试套件(cli-flag-validation.test.ts)逐一验证:

  1. 未知标志一律拒绝:search/detail各自维护KNOWN_FLAGS白名单,任何未声明的 flag 都会以UNKNOWN_FLAG错误退出。设计动机来自一次真实事故:在另一门户上,拼错的 flag 名被静默丢弃,导致一次请求把整个门户数据库(13862 条结果)当成匹配结果返回——被丢弃的过滤器改变了搜索结果却毫无报错。
  2. 数字参数严格整形校验:--jobage、--jobage-minutes、--page、--limit使用Number()(而非parseInt)解析,必须是大于等于 1 的整数,否则以BAD_ARG退出。这修复了parseInt("0.5") === 0导致f_TPR被静默丢弃的历史缺陷(测试中引用了 issue #371)。
  3. 冲突参数检测:--jobage与--jobage-minutes同时传入时以CONFLICTING_AGE_FLAGS退出;--location缺失以NO_LOCATION退出;detail缺 ID 以NO_ID退出;未知命令以BAD_CMD退出;运行期异常统一封装为INTERNAL_ERROR。

完整错误码速查:

错误码触发场景
NO_LOCATIONsearch缺少必填的--location/-l
CONFLICTING_AGE_FLAGS同时传了--jobage与--jobage-minutes
BAD_ARG数字参数非整数、小于 1 或非数字
UNKNOWN_FLAG传入了未声明的 flag
NO_IDdetail缺少<id\|url>参数
BAD_CMD未知命令
BAD_IDdetail的输入无法解析出职位 ID(含非 LinkedIn 域名 URL)
NOT_FOUND详情页返回 404
SEARCH_FAILED/DETAIL_FAILED对应命令运行期请求失败
INTERNAL_ERROR未捕获的运行时异常

所有错误均以单行 JSON 写入 stderr,退出码为1,与仓库其他门户 CLI 的错误契约保持一致。

八、常见问题与使用建议

  • 为什么--jobage与--jobage-minutes不能同时用:二者都用于构造同一个f_TPR时间窗参数,同时传入会产生歧义,CLI 选择显式报错而非猜一个值。
  • --limit与分页的关系:--page决定请求哪一页(每页固定 10 条,即start = (page-1)*10);--limit是客户端侧截断,即在解析出的卡片列表上slice(0, limit)(src/commands/search.ts),不会改变发往 LinkedIn 的请求。
  • 收到 429 怎么办:CLI 内置最多 6 次、最长约 8 秒延迟的指数退避重试,但仍建议降低请求频率、缩小--jobage窗口,遵守"个人使用、低频率"的边界。
  • detail报BAD_ID:确认输入是纯数字 ID、jobs/view/开头的 LinkedIn URL 或urn:li:jobPosting:URN,且主机确为 linkedin.com——申请外链(Greenhouse/Lever 等)无法用于查询。
  • 想快速验证正确性:直接运行bun test(在.agents/skills/linkedin-search/cli目录下)执行测试套件,覆盖 flag 校验、未知标志拒绝、f_TPR构造、--limit 0行为、ID 归一化、重试退避与请求超时等场景;运行bun run src/cli.ts --help(或search --help)可随时查看内嵌的完整用法帮助。

九、延伸阅读

  • 技能完整定义(触发条件、上下文与 ToS 说明):.agents/skills/linkedin-search/SKILL.md
  • CLI 官方说明(本文主体来源):.agents/skills/linkedin-search/cli/README.md
  • 数据端点与查询参数对照:/.agents/skills/linkedin-search/url-reference.md
  • CLI 入口与参数解析/校验:.agents/skills/linkedin-search/cli/src/cli.ts
  • 搜索命令实现:.agents/skills/linkedin-search/cli/src/commands/search.ts
  • 详情命令与 ID 归一化:.agents/skills/linkedin-search/cli/src/commands/detail.ts
  • 请求、解析与参数换算核心:.agents/skills/linkedin-search/cli/src/helpers.ts
  • 依赖与脚本声明:.agents/skills/linkedin-search/cli/package.json
  • 参数校验测试:.agents/skills/linkedin-search/cli/tests/cli-flag-validation.test.ts
  • AI 应用
  • AI 技能

【免费下载链接】ai-job-search

The job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.

项目地址:https://gitcode.com/GitHub_Trending/ai/ai-job-search
点击查看免费下载

相关推荐

上一篇:Apache DolphinScheduler FILE 参数详解:任务间文件传递的完整指南
下一篇:IoT-For-Beginners 智能语音计时器:Wio Terminal 基于 DMAC 与 Flash 的音频采集实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 11:14:57

长期记忆如何让AI真正“记住你”?

一、什么是长期记忆&#xff1f;长期记忆&#xff08;Long-term memory&#xff09;能够让智能体&#xff08;agent&#xff09;在不同对话、会话之间存储和调取信息长期记忆基于 LangGraph 的存储模块实现&#xff0c;该模块将数据保存为 JSON 文档&#xff0c;通过命名空间&a…

作者头像 李华
网站建设 2026/9/30 11:08:07

90DaysOfDevOps 第四天:Agile 与 DevOps 的本质差异与融合之道

文档/教程 【免费下载链接】90DaysOfDevOps This repository started out as a learning in public project for myself and has now become a structured learning map for many in the community. We have 3 years under our belt covering all things DevOps, including Pri…

作者头像 李华
网站建设 2026/9/30 11:07:58

Vue3 + VS Code 插件清单:从 Volar 到 ESLint 的工程化配置指南

开始切 Vue3 项目的那段时间&#xff0c;我做过最蠢的事就是把 VS Code 插件商店里的热门插件装了个遍&#xff0c;结果编辑器比电脑还卡&#xff0c;真正干活时总有几个插件在左下角疯狂报错。后来我重新梳理了一遍&#xff0c;才发现 Vue3 开发真正需要的插件其实就那十几个&…

作者头像 李华
网站建设 2026/9/30 11:05:42

一亩田客服咨询AI流量赋能,一亩田科技重塑智能体验新标杆

近期&#xff0c;由湖南改变生物科技有限公司主办、本因内酵未徕品牌协办的“生物科技健康论坛暨AI赋能大健康产业启动会”在长沙市步步高福鹏喜来登酒店隆重举行。活动以“AI流量赋能实体破局——中小企业增长峰会”为主题,汇聚全国大健康行业专家、中小企业负责人、机构代表及…

作者头像 李华
网站建设 2026/9/30 11:05:02

阿里国际站代运营的坑有哪些?新手老板必看的防套路清单

核心摘要代运营行业鱼龙混杂&#xff0c;“保效果”“低价包年”往往是套路的起点&#xff0c;签约前需重点审查服务流程和团队配置。判断代运营是否靠谱的关键&#xff0c;不在于对方承诺多少&#xff0c;而在于是否提供透明的数据汇报、明确的关键词和直通车操作方案。国际站…

作者头像 李华
网站建设 2026/9/30 11:03:23

网络编程实战:从socket入门到TCP服务调优与排障

搞网络编程的人&#xff0c;十个有八个是从socket入门的&#xff0c;又有九个在入门阶段被socket折磨过。这不是劝退&#xff0c;是事实。socket这套接口看着就那几步&#xff0c;socket、bind、listen、accept&#xff0c;但等到你真去做一个需要扛流量的服务时&#xff0c;就…

作者头像 李华