我做了一个 某直聘自动打招呼桌面助手,支持多账号、筛选岗位、实时日志
最近在投简历时发现一个痛点:每天要反复打开 某平台 直聘,搜索岗位、筛选城市/薪资/经验、点岗位、点立即沟通,动作高度重复。
于是我做了一个桌面版自动招呼助手:auto-resume。
它基于:
- Electron:桌面应用外壳
- React + Vite + TypeScript:前端界面
- Python FastAPI:本地后端服务
- Patchright / Playwright:浏览器自动化
- SQLite:本地任务和已招呼记录存储
- WebSocket:实时日志推送
说明:某平台 直聘服务条款不允许自动化操作,本项目仅供学习交流。请合理控制频率,账号风险自担。
一、项目效果
这个工具主要解决几个问题:
-
多账号可以同时跑
每个账号都有独立的 Chromiumuser_data_dir,cookie 和登录态互不影响。 -
可以自定义岗位筛选条件
支持关键词、城市、薪资、经验、学历、公司规模、黑名单关键词。 -
可以配置多个搜索子任务
比如先跑「前端开发」,再跑「React 开发」,每个子任务都有自己的招呼次数。 -
自动打招呼
程序会打开岗位列表,读取岗位卡片,过滤不合适的岗位,然后点击「立即沟通」。 -
防重复招呼
使用 SQLite 记录当天已经招呼过的岗位,同一个岗位当天不会重复处理。 -
实时日志
前端通过 WebSocket 订阅任务状态,能看到程序当前在做什么。 -
支持暂停、继续、停止、编辑任务
桌面端操作比较直观。
二、技术架构
整体架构如下:
auto-resume
├── Electron 桌面端
│ └── 加载 React 页面
│
├── React 前端
│ ├── 创建任务
│ ├── 编辑任务
│ ├── 控制开始 / 暂停 / 继续 / 停止
│ └── 展示实时日志和今日已招呼记录
│
├── FastAPI 后端
│ ├── REST API 管理任务
│ ├── WebSocket 推送状态
│ ├── TaskManager 管理多个任务
│ └── SQLite 存储任务和已招呼记录
│
└── Patchright / Playwright 自动化
├── 打开独立浏览器 profile
├── 等待用户登录
├── 构造 某平台 搜索 URL
├── 抓取岗位卡片
├── 过滤黑名单和已招呼岗位
└── 点击立即沟通
三、项目目录结构
auto-resume/
├── package.json
├── electron/
│ ├── main.cjs
│ └── preload.cjs
├── frontend/
│ ├── package.json
│ └── src/
│ ├── App.tsx
│ ├── api/client.ts
│ ├── store/useTasks.ts
│ ├── constants/cities.ts
│ └── components/
│ ├── NewTaskModal.tsx
│ ├── TaskCard.tsx
│ ├── TaskDetail.tsx
│ └── StatusBadge.tsx
└── backend/
├── requirements.txt
├── run_server.py
└── app/
├── main.py
├── api/tasks.py
├── schemas/task.py
├── core/
│ ├── config.py
│ └── logger.py
└── services/
├── boss_automator.py
├── task_manager.py
└── storage.py
几个核心文件:
| 文件 | 作用 |
|---|---|
frontend/src/components/NewTaskModal.tsx | 新建 / 编辑任务表单 |
frontend/src/components/TaskCard.tsx | 任务卡片和控制按钮 |
frontend/src/components/TaskDetail.tsx | 日志和已招呼岗位详情 |
backend/app/api/tasks.py | 任务相关 REST API 和 WebSocket |
backend/app/services/task_manager.py | 多任务调度和线程隔离 |
backend/app/services/boss_automator.py | 自动化核心逻辑 |
backend/app/services/storage.py | SQLite 持久化 |
四、核心功能拆解
1. 多账号隔离
每个任务都会生成独立的浏览器 profile:
backend/data/profiles/<task_id>/
这样每个账号的 cookie、登录状态、浏览器缓存都是独立的。
也就是说:
- 账号 A 登录后,不会影响账号 B
- 每个任务可以对应一个 某平台 账号
- 下次启动同一个任务时,可以复用之前的登录态
核心逻辑在 boss_automator.py 中:
self._ctx = await self._pw.chromium.launch_persistent_context(
user_data_dir=str(_profile_dir(self.task_id)),
channel="chromium",
headless=False,
no_viewport=True,
locale="zh-CN",
timezone_id="Asia/Shanghai",
)
这里用的是 launch_persistent_context,它和普通无痕上下文不同,会把用户数据持久化到本地目录。
2. 手动登录 + 自动复用登录态
第一次启动任务时,程序会打开浏览器,让用户手动扫码或输入账号密码登录。
登录成功后,会写入一个登录标记:
backend/data/profiles/<task_id>/.login_ok
后续判断这个任务是否登录过,就可以通过这个 marker 来判断。
这样做的好处是:
- 不需要在程序里保存账号密码
- 登录动作仍然由用户自己完成
- cookie 由浏览器 profile 自己管理
3. 多子任务配置
创建任务时可以配置多个子任务。
例如:
子任务 1:关键词 React,城市深圳,目标 20 次
子任务 2:关键词 前端开发,城市广州,目标 20 次
子任务 3:关键词 Vue,城市远程,目标 10 次
后端数据结构:
class JobFilter(BaseModel):
keyword: str
city: Optional[str] = None
salary: Optional[str] = None
experience: Optional[str] = None
degree: Optional[str] = None
scale: Optional[str] = None
exclude_keywords: List[str] = []
class SubTask(BaseModel):
filter: JobFilter
limit: int = 10
任务会按顺序执行:
- 先跑第一个子任务
- 达到该子任务次数后,进入下一个子任务
- 所有子任务跑完后,任务结束
- 如果达到每日总上限,也会提前结束
4. 自动构造 某平台 搜索地址
程序会根据筛选条件构造 某平台 搜索 URL。
比如:
params = {
"query": f.keyword,
"city": city_code,
}
如果用户选择了薪资、经验、学历、公司规模,也会继续拼接:
if f.salary:
params["salary"] = f.salary
if f.experience:
params["experience"] = f.experience
if f.degree:
params["degree"] = f.degree
if f.scale:
params["scale"] = f.scale
最终得到类似:
https://www.zhipin.com/web/geek/job?query=前端开发&city=101280600&salary=405
5. 岗位列表抓取
进入搜索页后,程序会等待岗位列表出现,然后从页面里抓取岗位卡片。
抓取的信息包括:
- 岗位标题
- 公司名称
- 岗位标签
- 详情链接
- 是否已经沟通过
抓取后会进入过滤阶段。
6. 黑名单过滤
每个子任务可以配置黑名单关键词。
比如:
外包, 外派, 实习, 驻场
程序会把岗位标题、公司名、标签拼成一段文本,然后判断是否命中黑名单。
命中后直接跳过。
def _matches_blacklist(text: str, blacklist: List[str]) -> bool:
text = (text or "").lower()
return any(kw.lower() in text for kw in blacklist if kw.strip())
7. 已招呼幂等处理
为了避免重复打招呼,项目使用 SQLite 记录当天已招呼过的岗位。
表结构大致是:
CREATE TABLE IF NOT EXISTS greeted_jobs(
task_id TEXT NOT NULL,
job_key TEXT NOT NULL,
day TEXT NOT NULL,
greeted_at TEXT NOT NULL,
title TEXT,
company TEXT,
sub_index INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (task_id, job_key, day)
);
重点是这个主键:
PRIMARY KEY (task_id, job_key, day)
它保证同一个任务、同一个岗位、同一天只会记录一次。
即使页面刷新、翻页或重新启动任务,也不会重复招呼同一个岗位。
8. 点击立即沟通
过滤通过后,程序会:
- 点击左侧岗位卡片
- 等待右侧详情加载
- 找到「立即沟通」按钮
- 点击按钮
- 如果出现弹窗,填写或确认招呼语
- 关闭弹窗
- 记录已招呼岗位
招呼语可以自定义多条:
您好,我对贵司这个岗位很感兴趣,希望能进一步沟通。
您好,看了岗位描述比较匹配,方便的话可以聊一下。
程序每次会随机选择一条,让行为不要太机械。
9. 实时日志推送
后端通过 WebSocket 推送任务状态和日志。
接口在:
/api/tasks/ws/{task_id}
前端订阅后,可以实时收到:
- 当前状态
- 已招呼数量
- 当前子任务
- 最新处理岗位
- 日志列表
这也是为什么桌面端可以看到程序实时运行过程。
10. Windows 下的多任务隔离
这个项目里有一个比较关键的点:每个自动化任务跑在独立线程里。
原因是 Windows 上 Playwright 启动浏览器需要兼容子进程事件循环。
任务管理器里做了处理:
if sys.platform == "win32":
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(coro)
这样每个任务都有自己的线程和事件循环。
好处:
- 多任务可以并行
- 浏览器操作不会阻塞 FastAPI 主循环
- WebSocket 推送仍然回到主事件循环执行
五、安装和运行
1. 环境要求
需要准备:
- Python 3.10+
- Node.js 18+
- Chrome / Chromium
2. 克隆项目
git clone https://github.com/wangrujun2016/auto-resume.git
cd auto-resume
3. 安装前端和 Electron 依赖
npm run install:all
4. 安装 Python 后端依赖
Windows:
cd backend
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python -m patchright install chromium
cd ..
macOS / Linux:
cd backend
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python -m patchright install chromium
cd ..
5. 启动开发模式
Windows:
npm run dev
macOS:
npm run dev:mac
启动后会同时运行三个服务:
| 服务 | 说明 |
|---|---|
| FastAPI | 127.0.0.1:8765 |
| Vite | 127.0.0.1:5173 |
| Electron | 桌面应用窗口 |
六、使用步骤
第一步:新建账号任务
打开应用后,点击左侧的「新建账号任务」。
需要填写:
- 任务 / 账号别名
- 搜索关键词
- 城市
- 薪资
- 经验
- 学历
- 公司规模
- 黑名单关键词
- 本子任务次数
- 自定义招呼语
- 间隔秒数
- 每日总上限
- 点开详情后的等待秒数
其中必填项主要是:
- 任务名称
- 搜索关键词
第二步:配置子任务
如果你只想跑一个关键词,保留一个子任务即可。
如果你想分批跑多个方向,可以继续添加子任务。
比如:
子任务 1:Java 后端,深圳,20 次
子任务 2:Spring Boot,广州,20 次
子任务 3:后端开发,杭州,20 次
程序会按顺序执行。
第三步:登录账号
点击任务卡片上的「登录」或「开始」。
首次运行会弹出浏览器窗口。
你需要手动完成 某平台 登录:
- 扫码登录
- 账号密码登录
登录成功后,程序会保存浏览器 profile。
下次再跑同一个任务,就不需要重复登录。
第四步:开始自动招呼
登录完成后,点击「开始」。
程序会自动:
- 打开 某平台 首页
- 检查登录状态
- 跳转搜索页
- 抓取岗位列表
- 跳过黑名单岗位
- 跳过今日已招呼岗位
- 点击岗位详情
- 点击「立即沟通」
- 发送招呼语
- 写入 SQLite 记录
- 等待设定间隔后处理下一个岗位
第五步:查看日志
点击任务卡片,可以进入任务详情。
这里可以看到:
- 实时运行日志
- 今日已招呼岗位
- 当前任务状态
- 已发送数量
如果某个岗位被跳过,日志里也会说明原因。
第六步:暂停 / 继续 / 停止
任务运行中可以随时操作:
- 暂停:当前任务暂时不继续处理新岗位
- 继续:恢复运行
- 停止:结束当前自动化任务
- 编辑:修改任务配置,下次启动生效
- 删除:删除任务和对应浏览器 profile
七、打包桌面应用
Windows 打包
$env:CSC_IDENTITY_AUTO_DISCOVERY="false"
npm run dist:win
产物在:
release/
常见产物:
release/某平台自动招呼助手-Setup-0.1.0.exe
release/win-unpacked/
macOS 打包
npm run dist:mac
也可以只打当前架构:
npm run dist:mac:dmg
产物类似:
release/某平台自动招呼助手-0.1.0-x64.dmg
release/某平台自动招呼助手-0.1.0-arm64.dmg
八、本地数据存储位置
开发环境下,数据主要在:
backend/data/
包括:
backend/data/auto_resume.sqlite
backend/data/profiles/<task_id>/
打包后,不同系统的数据路径不同:
| 平台 | 路径 |
|---|---|
| Windows | %APPDATA%\AutoResume\ |
| macOS | ~/Library/Application Support/AutoResume/ |
| Linux | ~/.local/share/AutoResume/ |
如果想让某个账号重新登录,删除对应的 profile 即可。
九、常见问题
1. 为什么点击不到「立即沟通」?
某平台 前端 DOM 可能会变。
如果页面结构变了,需要调整:
backend/app/services/boss_automator.py
里面的选择器逻辑。
2. 为什么登录超时?
默认等待登录时间是 10 分钟。
如果 10 分钟内没有完成登录,任务会报登录超时。
可以调整:
_wait_for_login(timeout_seconds=600)
3. 可以隐藏浏览器运行吗?
理论上可以把:
headless=False
改成:
headless=True
但不推荐。
因为首次登录需要人工操作,而且招聘网站对 headless 浏览器通常比较敏感。
4. 为什么要设置间隔?
不要高频点击。
项目里默认会有间隔和随机抖动,用来降低连续机械操作的特征。
建议合理设置频率,比如 30 秒以上。
5. 会不会重复打招呼?
正常不会。
项目会用 SQLite 记录当天已经招呼过的岗位,同一天同任务下不会重复处理同一个岗位。
十、项目亮点总结
这个项目虽然是一个小工具,但里面涉及到的东西还挺完整:
- Electron 桌面应用
- React 任务管理界面
- FastAPI 本地服务
- WebSocket 实时通信
- SQLite 本地持久化
- Playwright / Patchright 浏览器自动化
- 多账号浏览器 profile 隔离
- 多线程 + 多事件循环任务调度
- 任务暂停、继续、停止
- 自动化幂等处理
- Windows / macOS 打包
如果你正在学习 Electron + Python 后端 + 浏览器自动化,这个项目也可以作为一个完整案例来参考。
十一、源码和下载
创作不易,跪求 star,麻烦点个小星星~
源码地址:
下载链接:
提取码:
8bkt