LinkedIn recruiter message automation toolkit
Project description
recruiter-msg
LinkedIn 招聘者消息自动化工具包 —— 基于 Selenium 的 LinkedIn Recruiter 消息批量发送、候选人筛选与去重管理工具。
功能特性
- 批量消息发送:从 Excel 读取候选人列表,自动导航至 LinkedIn Recruiter 页面并发送定制消息
- 智能去重:基于 DiskCache + JSON 断点的双重去重机制,避免重复发送
- 候选人筛选:支持基于关键词和 LLM 的姓名/职位评估,自动过滤非技术岗位
- URN 缓存:自动缓存公开 URL 与 Recruiter URN 的映射关系,加速后续访问
- 简历提取:从 LinkedIn 页面自动提取候选人资料文本并保存
- LLM 集成:支持 OpenAI 兼容 API(DeepSeek、Ollama 等)进行智能评估
- 模板变量:消息正文支持
{name}、{title}、{email}、{linkedin_url}动态替换 - CLI 命令行:提供完整的 Typer CLI 工具,开箱即用
安装
从 PyPI 安装
pip install recruiter-msg
快速开始
第一步:初始化配置
recruiter-msg init
此命令会在当前目录生成 .env 文件,请编辑填入你的 LLM API 配置:
# LLM 配置
LLM_API_KEY=your-api-key-here
LLM_BASE_URL=http://api.openai.com/v1
LLM_MODEL=deepseek-v3.1-terminus-chat
LLM_TIMEOUT=120
LLM_MAX_RETRIES=3
如果不使用 LLM 评估功能,可跳过此步骤。
第二步:准备 Cookie 文件
本工具需要 LinkedIn Recruiter 的登录 Cookie 来访问招聘者页面:
- 用浏览器登录 LinkedIn Recruiter
- 安装 Cookie 导出浏览器插件(推荐 EditThisCookie 或 Cookie-Editor)
- 导出 Cookie 为 JSON 格式
- 保存为
cookie.json到当前目录(或通过--cookie-file指定路径)
第三步:准备候选人 Excel 文件
创建一个 Excel 文件(.xlsx),包含以下列:
| 列名 | 说明 |
|---|---|
url |
LinkedIn 个人资料 URL(公开链接或 Recruiter 链接) |
tag |
标签,用于过滤(如 "agent") |
recommend_title |
推荐职位标题,同时用作消息主题 |
email_content |
消息正文内容,支持模板变量替换 |
模板变量:在 email_content 中可使用以下变量,发送时自动替换:
{name}— 候选人姓名(从 LinkedIn 页面解析){title}— 候选人当前职位{email}— 候选人邮箱(需配置邮箱数据源){linkedin_url}— 候选人 LinkedIn URL
示例 Excel 数据:
| url | tag | recommend_title | email_content |
|---|---|---|---|
| https://linkedin.com/in/zhangsan | agent | 高级Java工程师 | 你好 {name},我们对你的背景很感兴趣... |
| https://linkedin.com/in/lisi | agent | AI算法工程师 | Hi {name},有一个 {title} 的机会想和你聊聊... |
第四步:运行自动化
# 基本运行(discover 模式,使用 URN 缓存)
recruiter-msg run --excel ./candidates.xlsx --tag agent --mode discover
# 公开链接模式(首次运行,自动发现 URN 并缓存)
recruiter-msg run --excel ./candidates.xlsx --tag agent --mode public-url
# Recruiter 链接模式(Excel 中直接提供 talent/profile 链接)
recruiter-msg run --excel ./candidates.xlsx --tag agent --mode recruiter-url
# 启用 LLM 评估(智能筛选候选人)
recruiter-msg run --excel ./candidates.xlsx --llm --mode discover
# 指定切片范围(只处理第 5-10 位候选人)
recruiter-msg run --excel ./candidates.xlsx --start 4 --end 10
# 自定义延时(避免触发频率限制)
recruiter-msg run --excel ./candidates.xlsx --delay-min 2.0 --delay-max 5.0
# 无头模式(不显示浏览器窗口)
recruiter-msg run --excel ./candidates.xlsx --headless
# 详细日志输出
recruiter-msg run --excel ./candidates.xlsx -v
处理模式说明
| 模式 | 适用场景 | 说明 |
|---|---|---|
discover |
已有 URN 缓存 | Excel 提供公开链接,先查 URN 缓存,有则直接跳转招聘者页面,无缓存则失败 |
public-url |
首次运行 | Excel 提供公开链接,从公开页面导航并自动发现招聘者链接,缓存 URN |
recruiter-url |
仅有招聘者链接 | Excel 提供招聘者链接,直接访问并回填 URN 缓存 |
其他命令
# 列出 Excel 中的候选人
recruiter-msg list-candidates --excel ./candidates.xlsx --tag agent --limit 20
# 检查某个标识符是否已发送
recruiter-msg check-duplicate "https://linkedin.com/in/zhangsan"
# 初始化 .env 配置文件(使用 --force 覆盖已有文件)
recruiter-msg init --force
项目架构
目录结构
recruiter-msg/
├── pyproject.toml # 项目构建配置与依赖声明
├── README.md # 项目文档
├── LICENSE # MIT 许可证
├── src/recruiter_msg/
│ ├── __init__.py # 包入口,导出所有公共 API
│ ├── models.py # 数据模型定义
│ ├── interfaces.py # 抽象接口(策略模式)
│ ├── exceptions.py # 自定义异常
│ ├── checker.py # 去重检查器实现
│ ├── datasource.py # 候选人数据源实现
│ ├── parser.py # LinkedIn 页面解析器
│ ├── navigator.py # LinkedIn 页面导航器
│ ├── sender.py # LinkedIn 消息发送器
│ ├── evaluator.py # 评估器实现
│ ├── strategy.py # 消息策略实现
│ ├── orchestrator.py # 核心编排器与工厂函数
│ ├── resume.py # 简历文本提取
│ ├── llm.py # LLM 客户端
│ └── cli.py # CLI 命令行入口
└── tests/ # 测试目录
核心设计模式
本项目采用 策略模式(Strategy Pattern) 设计,所有核心功能通过抽象接口定义,具体实现可替换:
┌─────────────────────────────────────────────────────────────┐
│ CLI (cli.py / Typer) │
│ recruiter-msg run / list / ... │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Orchestrator (orchestrator.py) │
│ LinkedInAutomationOrchestrator │
│ │
│ process_candidate() / process_candidate_urn() │
│ process_candidate_direct_urn() │
│ │
│ 编排完整流程:导航 → 解析 → 评估 → 去重 → 生成消息 → 发送 │
└──┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬─────────┘
│ │ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
┌─────┐┌─────┐┌─────┐┌─────┐┌─────┐┌─────┐┌─────┐┌─────┐
│Nav ││Parse││Name ││Title││Dedup││Msg ││Send ││Data │
│ ││ ││Eval ││Eval ││Chk ││Strat││ ││Src │
└──┬──┘└──┬──┘└──┬──┘└──┬──┘└──┬──┘└──┬──┘└──┬──┘└──┬──┘
│ │ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
接口层 (interfaces.py) — 9 个抽象基类
接口与实现对照表
| 抽象接口 | 实现类 | 职责 |
|---|---|---|
ProfileParser |
LinkedInProfileParser |
解析 LinkedIn 个人资料页面 |
MessageStrategy |
ExcelMessageStrategy |
基于模板生成消息内容 |
DuplicateChecker |
DiskCacheDuplicateChecker |
基于 DiskCache 的去重 |
JsonCheckpointDuplicateChecker |
基于 JSON 文件的断点去重 | |
CompositeDuplicateChecker |
组合多个去重检查器 | |
Navigator |
LinkedInNavigator |
LinkedIn 页面导航与交互 |
MessageSender |
LinkedInMessageSender |
消息输入与发送 |
NameEvaluator |
LLMNameEvaluator |
基于 LLM 的姓名分类评估 |
TitleEvaluator |
KeywordTitleEvaluator |
基于关键词的职位筛选 |
LLMTitleEvaluator |
基于 LLM 的技术岗位识别 | |
CandidateDataSource |
ExcelCandidateDataSource |
从 Excel 加载候选人数据 |
EmailDataSource |
(需用户自行实现) | 邮箱数据获取 |
数据模型
Config 全局配置(状态关键词、主题关键词、招聘者标识)
Candidate 候选人完整数据(URL、ID、URN、消息标题/正文、标签)
CandidateProfile 从页面解析的候选人资料(姓名、职位、LinkedIn URL)
MessageContent 消息内容(标题 + 正文)
MessageThread 消息线程(发件人、主题、状态、日期、预览)
处理流程
以 discover 模式为例,单个候选人的完整处理流程:
1. 查询 URN 缓存(公开 URL → Recruiter URN)
2. 导航至 Recruiter 个人资料页
3. 解析候选人资料(姓名、职位)
4. 评估姓名(可选,LLM 判断国籍过滤)
5. 评估职位(关键词过滤或 LLM 判断技术岗)
6. 保存简历文本(可选)
7. 检查消息历史(避免重复发送)
8. 检查缓存去重
9. 检查消息按钮可用性
10. 打开消息对话框
11. 生成消息内容(模板变量替换)
12. 发送消息
13. 标记为已发送(写入去重缓存 + 断点文件)
编程接口使用
除了 CLI 命令行,也可以在 Python 代码中直接调用:
from recruiter_msg import (
Config,
LinkedInAutomationOrchestrator,
create_default_orchestrator,
LLMClient,
)
# 1. 创建配置
config = Config(
status_keywords=["Accepted", "Deleted"],
subject_keywords=["Agent"],
recruiter="chandler",
)
# 2. 创建编排器和数据源(需要先初始化 Selenium driver)
orchestrator, data_source = create_default_orchestrator(
driver=driver,
config=config,
excel_path="./candidates.xlsx",
tag_filter="agent",
use_llm=True, # 启用 LLM 评估
urn_cache_dir="./cache_dir", # URN 缓存目录
resume_dir="./resume", # 简历保存目录
)
# 3. 加载候选人
candidates = data_source.fetch_candidates(tag_filter="agent")
# 4. 逐个处理
for candidate in candidates:
result = orchestrator.process_candidate_urn(candidate)
print(f"{'成功' if result else '失败'}: {candidate.linkedin_url}")
自定义实现
所有核心组件均可替换,只需实现对应的抽象接口:
from recruiter_msg import DuplicateChecker, MessageStrategy, Candidate, CandidateProfile, MessageContent
# 自定义去重检查器
class RedisDuplicateChecker(DuplicateChecker):
def is_duplicate(self, identifier: str, content_key: str) -> bool:
# 使用 Redis 实现
...
def mark_as_sent(self, identifier: str, content_key: str) -> None:
...
# 自定义消息策略
class AIMessageStrategy(MessageStrategy):
def generate_content(self, candidate: Candidate, profile: CandidateProfile) -> MessageContent:
# 使用 LLM 动态生成消息
...
# 注入到编排器
orchestrator = LinkedInAutomationOrchestrator(
driver=driver,
duplicate_checker=RedisDuplicateChecker(),
message_strategy=AIMessageStrategy(),
...
)
LLM 客户端配置
LLMClient 支持任何 OpenAI 兼容的 API 服务:
from recruiter_msg import LLMClient
# 方式一:通过 .env 文件配置
# LLM_API_KEY=sk-xxx
# LLM_BASE_URL=https://api.deepseek.com/v1
# LLM_MODEL=deepseek-chat
# 方式二:直接传参
client = LLMClient(
api_key="sk-xxx",
base_url="https://api.deepseek.com/v1",
model="deepseek-chat",
timeout=120,
max_retries=3,
)
# 方式三:使用本地 Ollama
client = LLMClient(
api_key="ollama",
base_url="http://localhost:11434/v1",
model="qwen2.5:7b",
)
# 调用
response = client.chat("你是助手", "你好")
result = client.chat_json("你是分类专家", "判断以下职位是否为技术岗:Java工程师")
常见问题
Q: Cookie 过期怎么办?
重新导出 LinkedIn Cookie 并替换 cookie.json 文件即可。LinkedIn Cookie 通常有效期为数天至数周。
Q: 如何避免被 LinkedIn 限制?
- 使用
--delay-min和--delay-max设置合理的操作间隔(建议 2-5 秒) - 使用
--start和--end分批处理候选人 - 避免短时间内大量操作
Q: URN 缓存有什么用?
LinkedIn Recruiter 页面使用 URN(如 ACoAABxxx)标识候选人,而 Excel 中通常只有公开链接。URN 缓存将公开 URL 映射到 Recruiter URN,避免每次都需要从公开页面重新发现 Recruiter 链接,大幅提升处理速度。
免责声明
本工具仅供学习和研究目的使用。
-
服务条款:本工具的自动化操作可能违反 LinkedIn 的服务条款(Terms of Service)和用户协议。使用本工具即表示你知悉并自行承担因违反服务条款而导致的一切后果,包括但不限于账号被限制、封禁或法律追诉。
-
合规使用:使用者应确保其使用方式符合所在地区的法律法规,包括但不限于数据隐私法(如 GDPR、CCPA 等)、反垃圾信息法以及劳动就业相关法规。
-
无担保:本工具按"原样"(AS IS)提供,不作任何明示或暗示的担保,包括但不限于适销性、特定用途适用性和非侵权性。作者不对使用本工具所产生的任何直接、间接、附带、特殊或后果性损害承担责任。
-
风险自担:使用者应当充分理解本工具的功能和潜在风险,自行评估并承担所有使用风险。作者不对因使用本工具而导致的任何损失、数据泄露、账号问题或其他不良后果承担任何责任。
-
非商业用途:本工具不鼓励也不支持任何形式的商业滥用、大规模自动化骚扰或垃圾信息发送行为。
-
修改与终止:作者保留随时修改、更新或终止本工具的权利,无需事先通知。
使用本工具即表示你已阅读、理解并同意以上免责声明的所有条款。如果你不同意,请勿使用本工具。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file recruiter_msg-0.1.0.tar.gz.
File metadata
- Download URL: recruiter_msg-0.1.0.tar.gz
- Upload date:
- Size: 21.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
956b4e19b1cb4e7b3be156324d0a8ce6ce24d4bbb9be5c2365dcbdbf35a5d5dd
|
|
| MD5 |
1d324297a2e56a84f92ccb314bfa9752
|
|
| BLAKE2b-256 |
295c8d140cd649ef26534e68d07888544b24349c905a7c528574fba45ba4342b
|
File details
Details for the file recruiter_msg-0.1.0-py3-none-any.whl.
File metadata
- Download URL: recruiter_msg-0.1.0-py3-none-any.whl
- Upload date:
- Size: 28.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b54e383a83f5f15fef154a1e9bcc5f91103f09722f1d5d3908acca5c2d3928e5
|
|
| MD5 |
38f3dcf7d9c0945339b31e5849eb8fd4
|
|
| BLAKE2b-256 |
6017e04af4dc51d4e1d40090ab465ee3cc4d823b828a187858c960d961615594
|