christopherdraper/linkedin-easy-apply
GitHub: christopherdraper/linkedin-easy-apply
一个由 Claude AI 驱动的自动化求职工具,通过三级任务队列实现跨 LinkedIn 及多 ATS 平台的职位搜索、评分、求职信生成与智能表单填写。
Stars: 0 | Forks: 0
# job-apply
跨 LinkedIn Easy Apply 及外部 ATS 平台(Greenhouse、Workday、Ashby、Lever、SmartRecruiters、Paylocity、Workable、Comeet、PageUp)的自动化求职搜索与申请工具,由 Claude 提供支持。
一个三级队列会逐步升级处理任务:
- **Q1 (batch)** 搜索 LinkedIn,根据你的个人资料对每个职位发布进行评分,生成量身定制的求职信,并通过 Easy Apply 或导航外部 ATS 表单进行提交。
- **Q2 (autonomous retry)** 提取 Q1 中分数超过阈值的失败申请,并结合 Playwright 以及 Claude 页面分析进行重试。处理邮箱验证码(Gmail IMAP)、验证码(2captcha 或 capsolver)以及特定 ATS 的异常情况。
- **Q3 (escalation)** 将 Q2 无法完成的任务通过 Web 仪表板呈现,并提供预先计算好的答案,以便你在不到一分钟内手动完成申请。
Flask 仪表板会跟踪市场职位发布量、每份申请的报告以及 Q2/Q3 队列。
有关日常操作,请参阅 [USAGE.md](USAGE.md)。有关内部架构,请参阅 [CLAUDE.md](CLAUDE.md)。
## 使用 Claude Code 进行设置(推荐)
如果你已经安装了 [Claude Code](https://claude.com/claude-code),最快的方法是让它驱动设置过程。
1. Clone 该代码仓库并 `cd` 进入其中。
2. 在该目录下打开 Claude Code。
3. 粘贴此 prompt:
## 环境要求
- Python 3.9+
- 一个 LinkedIn 账户
- 一个 Anthropic API key (https://console.anthropic.com)
- 可选但推荐:
- 带有 App Password 的 Gmail 账户(用于 ATS 邮箱验证码)
- 2captcha 或 capsolver API key(用于 ATS 验证码挑战)
- 一个 SOCKS5 住宅代理(仅在针对 SmartRecruiters / Incapsula WAF 时需要)
## 安装说明
```
# 1. Clone repo
git clone https://github.com/christopherdraper/linkedin-easy-apply
cd linkedin-easy-apply
# 2. Install Python dependencies
pip install -r requirements.txt # or: pip install playwright anthropic python-docx flask
# 3. Install Playwright 使用的 Chromium build
playwright install chromium
```
## 首次运行指南
最快上手的方法是让 Claude 从你的简历中引导生成你的个人资料。
### 第 1 步:创建运行时目录并复制个人资料模板
```
mkdir -p ~/.local/share/job-apply
cp profile_template.json ~/.local/share/job-apply/profile.json
```
### 第 2 步:让 Claude 根据你的简历填写个人资料
在代码仓库目录中打开 Claude Code。放入你的简历(PDF 或 .docx),并向它提供以下 prompt:
Claude 将生成一份完整的 `profile.json`。在继续之前请大致检查一遍。这份个人资料是所有表单填充的唯一事实来源,因此准确的 `screening_answers` 键比其他任何事情都重要。
### 第 3 步:设置你的环境变量
请参阅下方的 [环境变量清单](#environment-variable-checklist)。
### 第 4 步:捕获 LinkedIn 会话
LinkedIn 会将会话 cookie 绑定到你登录时的 IP 地址,因此请从运行脚本的同一台机器上进行登录。运行一次此命令:
```
python -c "
from playwright.sync_api import sync_playwright
from pathlib import Path
session_file = Path.home() / '.local/share/job-apply/sessions/linkedin.json'
session_file.parent.mkdir(parents=True, exist_ok=True)
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
context = browser.new_context()
page = context.new_page()
page.goto('https://www.linkedin.com/login')
input('Log in in the browser, then press Enter...')
context.storage_state(path=str(session_file))
browser.close()
"
```
如果你是在一台 headless 服务器上运行,请安装 Xvfb 外加一个 VNC server,将 `DISPLAY=:99` 指向虚拟显示器,并通过 VNC 运行相同的代码片段。会话每隔几周就会过期。当脚本报告会话过期时重新运行此命令。
### 第 5 步:进行一次 dry run
不会提交任何内容。确认评分、求职信生成和表单发现功能正常工作。
```
python job_search_apply.py --title "Senior DevOps Engineer" --dry-run
```
检查输出。你应该能看到找到的职位、AI 评分、生成到 `~/.local/share/job-apply/cover-letters/` 的求职信,以及记录到 `~/.local/share/job-apply/applications.json` 的决策。
### 第 6 步:首次正式批次执行
从小规模开始。五份申请,保守的阈值。
```
python job_search_apply.py --max-applications 5 --min-score 0.75
```
### 第 7 步:启动仪表板
```
python dashboard.py # http://localhost:5050
```
仪表板会显示市场职位发布量、带有匹配分数的申请历史记录、每个申请的审计页面、Q2/Q3 队列、成本统计和面试跟踪。完整参考请见 [DASHBOARD.md](DASHBOARD.md)。
### 第 8 步:处理 Q2 队列
匹配分数超过 0.70 阈值的 Q1 失败申请会自动排入 Q2。使用 autonomous retry agent 处理它们:
```
python assisted_apply_mcp.py --max 10
```
这就是完整的日常周期。
## 环境变量清单
唯一需要的环境变量是 Anthropic key。其他所有内容都存在于 `profile.json` 中,从而保持其作为唯一事实来源。
| 变量 | 必需 | 用途 |
|----------|----------|---------|
| `ANTHROPIC_API_KEY` | 是 | AI 评分、表单填充、求职信 |
```
echo 'export ANTHROPIC_API_KEY="sk-ant-..."' >> ~/.bashrc
source ~/.bashrc
```
### 基于个人资料的凭据
这些信息存在于 `profile.json` 的 `application_settings` 下,而不是作为环境变量。
| 字段 | 适用场景 | 获取方式 |
|-------|-------------|---------------|
| `gmail_app_password` | Greenhouse 和 PageUp 邮箱验证码 | Google 账号,安全,两步验证,App Passwords。为“Mail”生成一个。16 个字符,无空格。 |
| `captcha_api_key` | Lever (hCaptcha)、Eightfold (reCAPTCHA)、部分 Workday | 在 2captcha.com 或 capsolver.com 注册。添加额度。 |
| `captcha_service` | 同上 | `"2captcha"` 或 `"capsolver"`。默认为 `2captcha`。 |
| `proxy_rules` | SmartRecruiters (Incapsula WAF)。在其他情况下可选。 | `domain: socks5://host:port` 的字典。示例:`{"smartrecruiters.com": "socks5://127.0.0.1:1080"}`。 |
| `auto_create_accounts` | Workday 账号 | 布尔值。为 true 时,bot 会创建 ATS 账号并在本地存储凭据。 |
| `max_applications_per_day` | 总是 | 每日申请硬性上限。默认 10。可通过 `--max-applications` 覆盖单次运行的设置。 |
| `min_match_score` | 总是 | AI 匹配分数的底线(0.0 到 1.0)。代码中默认为 0.30,个人资料模板中为 0.75。可通过 `--min-score` 覆盖单次运行的设置。 |
`profile.json` 中的示例代码块:
```
"application_settings": {
"max_applications_per_day": 50,
"min_match_score": 0.75,
"gmail_app_password": "xxxxxxxxxxxxxxxx",
"captcha_api_key": "abcd1234...",
"captcha_service": "2captcha",
"auto_create_accounts": true,
"proxy_rules": {
"smartrecruiters.com": "socks5://127.0.0.1:1080"
}
}
```
## 运行
```
# 始终先进行 dry-run
python job_search_apply.py --title "Senior SRE" --dry-run
# 针对你 profile 中的每个 title 进行 Live batch
python job_search_apply.py --max-applications 50 --min-score 0.75
# Market snapshot(按 title 统计 postings 数量,无 applications)
python job_search_apply.py --market-snapshot
# 单个 external URL(bypasses LinkedIn)
python job_search_apply.py --external-url https://boards.greenhouse.io/company/jobs/123
# Q2 autonomous retry
python assisted_apply_mcp.py --max 20
python assisted_apply_mcp.py --list # show queue
python assisted_apply_mcp.py --job-id li_abc # retry a specific entry
# Dashboard
python dashboard.py # http://localhost:5050
```
### 常用参数 (Q1)
| 参数 | 默认值 | 描述 |
|------|---------|-------------|
| `--profile` | `~/.local/share/job-apply/profile.json` | 个人资料路径 |
| `--title` | 个人资料中的所有职位 | 搜索单个职位名称 |
| `--max-applications` | 来自个人资料 | 限制本次运行的提交数量 |
| `--min-score` | 来自个人资料 | 匹配分数底线 |
| `--dry-run` | false | 仅评分和记录日志 |
| `--external-url` | 无 | 申请单个非 LinkedIn URL |
| `--market-snapshot` | false | 统计每个职位的发布数量,不进行申请 |
| `--source` | `linkedin` | `linkedin`, `remoteok`, `hn`, `biotech`, `all` |
## 数据存储
所有数据均存储在本地。个人资料内容会发送至 Anthropic API 用于评分和表单填充。没有其他任何数据会离开本机。
| 数据 | 位置 |
|------|----------|
| 个人资料 | `~/.local/share/job-apply/profile.json` |
| LinkedIn 会话 | `~/.local/share/job-apply/sessions/linkedin.json` |
| 申请记录 | `~/.local/share/job-apply/applications.json` |
| Q2/Q3 队列 | `~/.local/share/job-apply/deep_apply_queue.json` |
| 市场快照 | `~/.local/share/job-apply/search_log.json` |
| 求职信 | `~/.local/share/job-apply/cover-letters/` |
| 失败表单调试转储 | `~/.local/share/job-apply/debug/` |
## 安全特性
- **Dry-run 标志** 允许你测试整个 pipeline 而不进行任何提交。
- **敏感字段中止** 如果表单要求填写 SSN、银行账户、护照或类似信息,会立即中止操作。
- **Prompt 注入检测** 在将职位描述和表单标签传递给 AI 之前对其进行扫描。
- **AI 响应验证** 将生成的答案限制在合理的长度内,并筛查注入内容。
- **已申请跟踪** 通过 `applications.json` 根据 URL 进行去重。
- **每日上限** 防止批处理失控。
## 接下来去哪
- [USAGE.md](USAGE.md) 涵盖了日常工作流、特定 ATS 说明、队列维护和故障排除。
- [DASHBOARD.md](DASHBOARD.md) 是 Web 仪表板的完整参考(每个部分、每个路由、数据源、面试跟踪)。
- [CLAUDE.md](CLAUDE.md) 是开发者参考:架构、handler 注册表、经验教训和贡献指南。
标签:Claude AI, Playwright, RPA, Web自动化, 求职自动化, 特征检测, 逆向工具