恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

BOSS直聘爬虫实战:从会话协议到简历接收的自动化链路

  • 首页
  • 资讯中心
  • /
  • BOSS直聘爬虫实战:从会话协议到简历接收的自动化链路

相关资讯

no-mistakes 文档所有权与 Agent 指南生成治理:单一事实来源、漂移防护与同步契约 2026/9/16 16:48:05
如何查看所有分支的CI状态?Worktrunk对GitHub/GitLab/Gitea集成完全指南 2026/9/16 16:48:05
WatchYourLAN 高级 ARP 扫描配置指南:IFACES、ARP_ARGS 与 ARP_STRS 实战详解 2026/9/16 16:48:05

最新资讯

在 Flue 中接入 Salesforce Marketing Cloud Engagement ENS 通道:签名校验、事件分发与无 Salesforce 测试指南
local-deep-research 前端回归修复解析:FastAPI 迁移后的状态一致性保障(changelog 6037)
基于Django的实验室设备管理系统:从数据建模到生产部署
PraisonAI 智能体性能监控实战指南:从 `@monitor_function` 到综合性能仪表盘
Android校内兼职APP源码解析:数据库设计、状态机与核心功能实现
从超级个体到超级团队:企业级AI Agent平台核心能力与实战解析

今日推荐

IoT-For-Beginners 智能语音计时器:Wio Terminal 基于 DMAC 与 Flash 的音频采集实战
基于MATLAB的CRI显色指数计算:从SPD光谱到Ra的完整流程
JSP+Servlet+MySQL博客系统源码部署与优化全攻略

本周热门

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化
Flutter应用改名全指南:从Android到iOS的配置与工具实践

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

BOSS直聘爬虫实战:从会话协议到简历接收的自动化链路

发布时间:2026/9/16 16:53:05
BOSS直聘爬虫实战:从会话协议到简历接收的自动化链路 简介针对招聘场景中重复性、高并发的沟通需求这份boss直聘爬虫工具包提供了可运行的Python脚本与配套配置面向需要批量获取牛人信息、自动打招呼、接收/请求简历的招聘者或爬虫学习者帮助减少手动点击与信息整理成本。压缩包共11个文件主体是py脚本另有5个txt文件用于存放代理列表、Cookie、学校名单等配置md说明文档解释使用方式png示意图展示界面或流程整体仅1.2MB轻量易部署。已有896人学习下载可作为爬虫入门或招聘自动化的参考实现。脚本按v1、v2模块划分包含代理获取、牛人列表抓取、Cookie配置、学校/985/211名单过滤等功能README与整理文本便于快速理解参数含义和运行逻辑。建议有一定Python基础、熟悉requests与解析库的读者研究后结合自身合法场景调整使用。1. boss直聘爬虫不是爬页面是爬一套会话协议把Boss直聘App抓包后逐层拆开会发现它的接口设计基本是会话型登录后的所有业务动作——看牛人、打招呼、请求简历、接受简历——都挂在同一个会话上下文里服务端只认会话中的用户态不认页面上的按钮。这就是可获取牛人信息、与牛人打招呼、接受牛人简历、请求牛人简历这类打包爬虫能成立的前提它不是去渲染HTML页面而是把App里的那套会话协议在Python里重放一遍。实际工作中这类需求最常见的落地场景是自建人才库或招聘流程自动化把平台上的候选人数据拉回来做结构化存储再把沟通动作串成定时任务。它和爬新闻、爬电商的网页爬虫完全是两套思路难点不在解析而在状态维护——会话过期、频率限制、动作时序任何一个环节断掉整个脚本就变成一台只会登录的机器人。下面按我平时搭这套东西的顺序从数据获取讲到动作回写。2. 获取牛人信息从列表到简历详情的字段链路2.1 先认清三个数据源推荐流、搜索接口、人才卡片刚拿到标题里那种zip包时不要急着跑先看它抓的是哪个数据源。Boss直聘上能获取牛人信息的入口至少有三种首页推荐流、主动搜索、以及从会话里展开的人才卡片。我在接这类项目时第一步一定是先抓包确认目标数据源的URL特征因为三个入口返回的JSON结构差异很大套错了解析模板就会大量丢字段。推荐流返回的是卡片数组每张卡片包含候选人的脱敏信息、当前职位、工作年限、技能标签但它不会给你完整简历——完整简历必须通过请求简历动作等对方授权。搜索接口则多一组query参数比如职位、城市、薪资范围适合做批量拉取。人才卡片是进过会话的人包含一个关键的geoId人才唯一标识后续打招呼、请求简历都以它为主键。所以一个可用的牛人信息爬虫本质上是在维护一张以geoId为核心的人才宽表。2.2 用 requests 拉取牛人卡片的最小请求假定你已经通过抓包拿到了会话token和接口路径用Python的requests库就能完成第一版数据拉取。注意这里的关键不是URL本身而是请求头里那几个业务字段——zp_st会话令牌、Cookie、User-Agent以及必须在header中携带的x-requested-with: XMLHttpRequest。下面的代码是经过脱敏处理的示意写法替换成你自己的base_url即可运行。import requests BASE_URL https://gateway.example.com/api # 换成抓包得到的真实网关地址 def fetch_recommend_cards(cookie: str, zp_st: str, page: int 1) - dict: headers { User-Agent: Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36, Cookie: cookie, x-requested-with: XMLHttpRequest, zp_st: zp_st, } params { page: page, pageSize: 20, cityCode: 100010000, # 城市编码从城市选择接口获取 jobType: 1001, # 岗位方向编码 } resp requests.get(f{BASE_URL}/recommend/cards, headersheaders, paramsparams, timeout10) resp.raise_for_status() return resp.json() data fetch_recommend_cards(cookieyour_cookie, zp_styour_token, page1) for card in data[data][cardList]: print(card[geoId], card[name], card[currentTitle])逻辑说明先声明BASE_URL为网关前缀再构造请求头。zp_st和Cookie是从App登录态里一起带出来的缺一不可——单独给Cookie但丢掉zp_st服务端会直接返回41001会话失效码。params里的cityCode、jobType决定你能看到哪些牛人这两个编码需要在App里先做一次城市和岗位筛选再从抓包里抄下来。参数说明pageSize建议在 1020 之间设太大容易被服务端判定为遍历行为timeout必须显式设置否则线程挂起时整个采集任务会卡死。返回的cardList里geoId是全局唯一的人才ID后续所有的打招呼、请求简历都要以它为载荷。2.3 牛人详情里的隐藏字段期望薪资、工作经历与简历状态列表卡片返的字段远不够简历解析用。我一般会拿到geoId后再请求一次详情接口把resumeDetail和careerList补进来。这个接口返回的内容里有四个字段是写数据库时必须单独建索引的字段名类型含义何时有值geoIdstring人才唯一ID始终存在resumeScoreint简历完整度0-100始终存在expectSalaryobject期望薪资区间与币种始终存在resumeOpenStatusint简历公开状态1公开 2匿名 3关闭决定能否直接查看lastActiveTimelong最后活跃时间戳判断候选人是否在线上注意resumeOpenStatus这个字段它直接决定了下一步能不能请求简历。值为 3关闭时就算你发出请求对方也不会收到完整版授权。所以我在爬虫里会先做一轮过滤resumeOpenStatus 1的进入可打招呼队列2的只做数据入库不发动作避免把招呼耗在一个不会回的用户身上。def parse_resume_detail(data: dict) - dict: detail data.get(data, {}) career detail.get(careerList, []) last_job career[0] if career else {} return { geo_id: detail.get(geoId), name: detail.get(name, ).replace(*, ), # 脱敏星号可保留也可去掉 current_company: last_job.get(brandName), current_title: last_job.get(title), expect_min: detail.get(expectSalary, {}).get(min), expect_max: detail.get(expectSalary, {}).get(max), resume_status: detail.get(resumeOpenStatus), last_active: detail.get(lastActiveTime), }这层的坑在于careerList是按时间倒序返回的下标 0 是最近一段经历但有些人会有多段经历时间重叠的情况直接取下标 0 可能拿到一段已结束的外包经历。稳妥做法是按endTime排序后再取第一条——如果是时间上仍在进行的那段经历endTime会是一个极大值比如1999999999000排序时把它放前面即可。3. boss直聘爬虫主动链路打招呼与请求牛人简历的接口规则3.1 为什么打招呼和请求简历要分开做请求很多人拿到zip里的脚本后误以为打招呼和请求简历是同一个按钮的两个参数其实服务端把它们设计成了两套独立接口。打招唒的本质是创建一条会话消息目标是触达候选人让对方点进对话请求简历则是发起一个授权申请需要对方点击同意后才产生完整简历数据。两者之间的依赖关系是请求简历可以单独发送但绝大多数情况下候选人只有在收到打招呼并产生会话后请求简历的到达率才高。所以实际任务编排上是串行的先发打招呼等候选人回复或进入会话chatStatus变为 1再发请求简历。如果你在走廊里看到一个爬虫脚本是同时发这两个请求的那基本是拿自己的账号风控在赌博。接口被拒时通常返回code: 21002含义是当前会话状态不允许此操作也就是你已经跳过了前置步骤。3.2 打招呼的请求体和模板变量打招呼接口需要提交的不只是geoId还有一个被很多新手忽略的字段——templateId。平台为了控制骚扰强度把打招呼文案做成了固定模板调用者必须传模板ID并且模板里可能有{jobTitle}、{companyName}这类变量需要你在请求体里一并填充。下面是一个可执行的打招呼函数def send_greeting(cookie: str, zp_st: str, geo_id: str, job_id: str) - bool: headers { Cookie: cookie, zp_st: zp_st, Content-Type: application/json, x-requested-with: XMLHttpRequest, } payload { geoId: geo_id, jobId: job_id, # 打招呼时附带的热招职位ID templateId: 1001, # 打招呼模板ID从模板接口预取 templateVars: { jobTitle: Python开发工程师, companyName: 示例科技有限公司 }, verifyToken: get_verify_token(geo_id), # 会话校验令牌 } resp requests.post( fhttps://gateway.example.com/api/chat/greeting, headersheaders, jsonpayload, timeout10 ) ret resp.json() if ret.get(code) 0: return True # 21002: 会话状态异常, 21003: 当日打招呼次数耗尽, 41001: 登录态过期 log_action(geo_id, greeting, ret.get(code)) return False逻辑说明templateVars里的变量必须和模板占位符一一对应多传一个变量或漏传一个服务端会返回10008模板渲染失败。verifyToken是从geoId详情接口里带出来的一个会话校验串它的有效期很短大约几分钟到几小时不等因此不能预先缓存太久建议在打招呼前实时取一次。参数说明jobId是发起打招呼时你这边挂出的职位ID不能随便填必须是当前账号在招且状态为招聘中的职位。templateId从模板列表接口拉取不同的行业类目对应不同模板建议让运营人员确认文案后再写进配置而不是在代码里硬编码。每天打招呼次数上限在接口返回头里会有X-Greeting-Limit字段脚本应当解析它并做本地计数。3.3 请求简历的 token 与时序要求请求简历的接口和打招呼不同它对时序有强校验必须在会话有效期内请求且两次请求同一geoId的间隔不能低于一个平台阈值常见是72小时。请求体的核心字段是resumeReqToken这个token不能自己生成必须从会话消息流中的某条系统提示里提取。def request_full_resume(cookie: str, zp_st: str, geo_id: str, msg_id: str) - bool: resume_token extract_resume_token(geo_id, msg_id) # 从消息内容中解析 payload { geoId: geo_id, msgId: msg_id, # 触发授权入口的系统消息ID resumeReqToken: resume_token, } resp requests.post( https://gateway.example.com/api/resume/request, headers{Cookie: cookie, zp_st: zp_st, Content-Type: application/json}, jsonpayload, timeout10 ) return resp.json().get(code) 0extract_resume_token这个函数不是AI生成的魔法方法它做的事其实就是从消息接口里索引到那条等你索要简历的系统卡片然后从卡片的actionUrl参数里用正则把resumeReqToken([a-f0-9]{32})捞出来。这个token是一次性使用的发起请求后无论成功与否都会失效所以必须做完即弃不能把旧token用在第二个候选人身上。这里要特别提醒一个容易踩的坑请求简历动作会内嵌一个countdown逻辑如果同一会话窗口里候选人已经主动投递过简历服务端返回值是code: 0但不会再次推送完整简历——你的请求被静默吞掉了。排查方法是看下一次轮询时该geoId是否有新的resumeId生成没有就说明没走通。4. 接受牛人简历boss直聘爬虫的消息通道与状态回写4.1 接收简历的通道不止一个接受牛人简历这个动作在爬虫里的真实含义是监听会话消息流并识别简历事件而不是页面上的那个接受按钮——因为当候选人主动投递或同意请求时平台会把一份完整简历推送到你的消息中心爬虫要做的就是第一时间把它接收下来并落库。共三个通道按优先级排列会话消息轮询接口 —— 候选人回复、投递、同意请求都会产生新消息简历变更通知接口 —— 已联系候选人更新简历时推送主动请求的回执 —— 你请求后对方同意的回调实际开发中我只会持续轮询第一个通道因为后两个事件最终都会以消息的形式出现在会话流里轮询一次就能全部捞到。轮询频率要克制我通常设置 15 秒一次而不是 1 秒一次——频率越高越容易被识别为脚本特征。import time def poll_resume_events(cookie: str, zp_st: str, since_msg_id: int 0): 轮询消息中心返回新消息列表 headers {Cookie: cookie, zp_st: zp_st} seen set() while True: params {sinceMsgId: since_msg_id, limit: 50} resp requests.get( https://gateway.example.com/api/chat/messages, headersheaders, paramsparams, timeout10 ) for msg in resp.json().get(data, {}).get(list, []): msg_id msg[msgId] if msg_id in seen: continue seen.add(msg_id) if msg.get(msgType) resume_delivery: save_resume_from_message(msg) since_msg_id max(since_msg_id, msg_id) time.sleep(15) # 轮询间隔宁慢勿快 check_session_alive(headers) # 顺便做心跳保活这段代码的核心是用sinceMsgId做增量游标避免每次轮询都返回全量历史消息。msgType resume_delivery是简历投递事件消息体内会直接携带一个resumeId和fileUrl是完整简历的数据源。注意seen集合只做单次运行期的去重真正的去重要靠数据库里对(geoId, resumeId)建唯一索引。参数说明limit不要超过 50超过部分平台会截断且不返回分页游标。since_msg_id必须持久化成文件或存Redis否则服务重启后会重复拉取全部历史消息导致大量重复入库。4.2 从消息正文解析简历附件并回写云端拿到resume_delivery消息后消息体的content字段是一段JSON字符序列里面嵌着简历的基础摘要字段包括姓名可能脱敏、当前公司、工作年限、学历以及最重要的cvUrl——一份临时签名的HTML简历地址。这个地址有效期很短常见是 30 分钟必须立即下载。下面给出回写逻辑def save_resume_from_message(msg: dict) - None: content json.loads(msg[content]) geo_id content[geoId] resume_id content[resumeId] cv_url content.get(cvUrl) # 1. 先在库里做幂等判断 if db.resume_exists(geo_id, resume_id): return # 2. 下载简历文件超时设大一些简历页可能比较大 r requests.get(cv_url, timeout30) r.raise_for_status() # 3. 落地与入库 bucket_path fresume/{geo_id}/{resume_id}.html upload_to_oss(bucket_path, r.content) db.save_resume(geo_id, resume_id, bucket_path, content)cvUrl链接的域名和主业务接口不是同一个域下载时不需要携带zp_st只需要一个有效的Cookie。但要注意防重试一旦平台检测到同一cvUrl被多次高频访问会在第一次访问后追加校验参数导致后续重试全部签名失效。所以下载动作最好是单次成功失败就放弃本次等下次消息轮询再取——不要重试。中间层还有一个状态流转要处理一份简历从请求中到已收到中间存在一个pending态。我会在数据库里用resume_state字段标记resume_state含义触发动作0未请求仅入库不发动作1已打招呼等待候选人响应2已请求简历等待授权3已收到简历下载并解析4简历过期/撤销重新请求或放弃这个状态机不是装饰品它直接决定爬虫下一步行为state1且超过48小时没有新消息就认为打招呼无效从任务队列中移除该geoIdstate2超过72小时仍无简历推送则标记为候选人未授权不再发起重复请求。5. 给boss直聘爬虫做限流与会话保活用分布式队列替代多线程这类爬虫最大的死因不是IP封禁而是会话过期。登录态失效后上面所有接口都会返回41001如果脚本里没有做会话感知它仍会拿旧token继续狂发请求造成不必要的账号异常。我自己的方案是不写多线程并发而是用一个Redis列表把待动作的geoId全排成队列由单消费者按固定间隔出队执行动作。单账号场景下这比多线程安全得多因为平台的风控模型对同一账号并发请求极其敏感。import redis import time r redis.Redis(hostlocalhost, port6379, db0) def worker_loop(cookie: str, zp_st: str): while True: # 从队列左边取一个候选人ID geo_id r.lpop(zhipin:todo:greeting) if geo_id: success send_greeting(cookie, zp_st, geo_id.decode(), job_id123456) record_metric(greeting, success) # 成功后进入请求简历队列按72小时去重窗口控制 if success: r.setex(fzhipin:req:{geo_id}, 72*3600, 1) time.sleep(random.uniform(20, 30)) # 随机间隔避免固定节拍 else: time.sleep(5)代码里的random.uniform(20, 30)是限流的关键——固定间隔比随机间隔更容易被序列异常检测器抓出来。你要不要用分布式队列取决于账号数量如果目标是 10 个以内的账号Redis 单消费者足够超过 10 个账号再上 Celery 或 RQ否则只是徒增运维复杂度风控模型照样能从UA和IP维度把你关联起来。落库做验证时不要只看成功请求数要看有效会话时长。我会在日志里用time.time()记录每次请求的时间戳之后写一个SQL查询统计每个账号单位小时内的请求分布如果某个小时请求次数超过 60就触发告警并让该账号冷却 1 小时。这个阈值不是什么神秘数据是按平台页面按钮人工点击频率估出来的——正常人一小时也点不了60次打招呼脚本超过这个值就是不合格。验证方法再补一个在维护窗口里手动把zp_st改成假值触发一次请求等返回41001后检查你是否能在 3 秒内收到告警邮件。能收到说明会话感知链路是通的收不到先检查心跳接口的返回码是否被解析而不是急着调限流参数。公序良俗层面提醒一句这套方案仅用于处理你有权处理的候选人数据别拿去骚扰或转售管理好自己的数据边界脚本才跑得长久。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号