十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LinkedIn员工数据爬虫实战:从登录态到反爬虫的完整技术拆解

LinkedIn员工数据爬虫实战:从登录态到反爬虫的完整技术拆解 简介基于公司名称抓取LinkedIn员工公开信息的开源爬虫项目面向需要批量获取企业人才结构的研究者、数据分析师及招聘人员也适合初步接触网络爬虫的开发者学习参考。项目以Python实现核心脚本linkedinSpider.py演示了从目标设定、网络请求、HTML解析到深度抓取与数据存储的完整流程涵盖搜索页与个人详情页两轮抓取可学习requests、BeautifulSoup、selenium等库的搭配使用以及应对验证码、IP限制、User-Agent检测等反爬机制的常见策略代码结构便于按需调整抓取字段与存储格式。压缩包共3个文件包含Python源码、README说明和.gitignore配置整体仅5KB结构精简适合快速阅读并二次开发。目前已有1593人学习下载适用于市场研究、招聘寻源、客户分析或学术数据采集等场景使用时应遵循LinkedIn服务条款平衡爬取频率并尊重用户隐私避免对目标网站造成异常压力。1. 项目概述与需求拆解1.1 这个爬虫到底是干什么的拿到这个LinkedinSpider项目的时候其实第一反应就是——这是一个典型的“公司维度”人才采集工具。输入一个公司名字程序自动去LinkedIn上搜索这个公司的员工列表然后把每个员工的档案信息姓名、职位、所在地、个人简介、工作经历等抓取下来保存成结构化数据。这套逻辑听起来简单但实际应用场景相当广。做招聘的猎头拿它做目标公司人才地图做B2B销售的人用它挖掘潜在客户联系人做投研的人用它看创业公司的团队背景做竞品分析的HR会用它来拆解对手的组织架构。哪怕是我自己也经常用类似思路去验证某个赛道里有哪些核心人物在职。和传统爬虫不太一样的是LinkedInSpider的核心输入不是一个个URL链接而是一个可能模糊甚至带歧义的公司名称。所以它的第一步其实是个搜索动作——先在LinkedIn上找到目标公司主页再顺着公司主页的员工列表去逐层翻页最后把员工数据落库。这个“输入名字直接出人”的交互模式决定了它在工程实现上比普通定向爬虫多一层“搜索-消歧-对齐”的逻辑。1.2 做这类项目前必须想清楚的四个问题在真正动手写代码前有几个核心决策点会直接影响项目天花板数据字段的取舍。你要拿到什么粒度只拿姓名和职位那非常简单但如果你想拿到邮箱、手机号这些非公开字段技术上就是另一条路——这条路通常既违法又违背LinkedIn的用户协议而且基本只能靠漏洞或者诱导手段来实现不建议碰。这个项目能合理抓取的边界就是LinkedIn公开页面上展示的信息。登录态的处理方式。LinkedIn绝大多数功能都要求登录完全匿名的爬虫在搜索环节就会被挡下来。登录态用Cookie维持还是用账号密码自动登录要不要做Cookie池这是整个项目里最容易翻车的环节。请求频率和流量控制。LinkedIn的风控在业界属于中上水平封IP、弹验证码、限制搜索次数都是家常便饭。单机跑还是分布式跑、多久请求一次、要不要设置代理池这些不做前置规划代码写得再优雅也活不过两个小时。输出数据的组织和去重逻辑。同名员工、同一员工多段工作经历、不同公司用同一邮箱前缀等情况在存储字段设计上不提前考虑后面做数据分析的时候会非常痛苦。这四个问题想清楚了项目的大体框架也就出来了。2. 技术选型与整体架构设计2.1 请求框架选哪个为什么LinkedInSpider这类项目在请求框架上的选择个人经验是分成两派。第一派是用纯请求库requests或httpx模拟接口调用。这种方式性能高、单位时间能跑出更多数据但缺点是极其脆弱——LinkedIn的前端是React架构接口返回的JSON结构变更频繁而且很多数据接口有加密参数csrf-token、匿名token等请求头漏一个字段就会被弹回登录页。每次LinkedIn更新前端代码你的解析逻辑就要跟着改一遍维护成本很高。第二派是用浏览器自动化框架比如Playwright或Selenium。这套方案模拟真实用户浏览器行为能自动执行JavaScript、绕过大部分基础的指纹检测稳定性比纯HTTP请求高不少。代价是并发量上不去跑1000个公司可能要挂机一整天。我自己的建议是按需组合。如果只是临时跑几十个公司直接用Playwright 无头浏览器就够了稳定压倒一切如果要长期、大规模地跑那就得走“浏览器模拟登录 抓取关键Cookie 纯requests调接口”的混合模式用浏览器的能力帮纯HTTP请求铺路。这个项目从标题上看是LinkedinSpider这样的轻量封包大概率走的是第一种思路用Python的requests或者类似库直接请求搜索页和数据接口配合手动维护的Cookie完成鉴权。实测下来对中小企业数量级的任务这种方案的性价比确实最高。2.2 数据落盘与字段设计员工数据的最终存储格式我经历过三个阶段的迭代先是一股脑全塞CSV后来发现同一公司成百上千人、每人十几段工作经历一张CSV表格很难表达改成JSONL每行一个JSON对象后单条档案的完整层级才能保留下来最后是SQLite存结构化字段方便后续做筛选和统计。实际项目里推荐保存的核心字段我整理成了对照表字段示例值来源员工姓名张三搜索结果页/档案页职位标题Senior Engineer搜索结果页/档案页所在地区California, US搜索结果页个人简介转型中的全栈工程师档案页当前公司Acme Corp搜索条件工作经历列表Acme Corp → Alibaba → Tencent档案页可选深挖个人主页URLlinkedin.com/in/xxx搜索结果页这里有个细节容易被忽略搜索列表页上已经能拿到姓名、职位、地区这些核心信息但个人简介和工作经历必须点进每个员工的详情页才能拿到。如果项目只抓列表页速度很快但信息单薄如果抓了详情页数据质量高但请求量成倍增加——每多抓一个字段对应的反爬风险系数也涨一截。需要根据实际需求来做平衡。2.3 任务队列的设计思路虽然标题里没写调度模块但跑过的人都明白这种按公司名批量跑的任务必须有一个任务队列来接单。最朴素的实现是维护一个company_list.txt每行一个公司名跑完一个删一个稍微进阶一点用Redis的有序集合做任务队列支持崩溃恢复、爬虫worker的横向扩展。分布式爬虫在LinkedIn这个场景其实被严重依赖。原因很简单单IP的请求频率上限就摆在那想提高吞吐量要么等IP冷却要么上代理池和多台机器。Twitter上曾有团队分享过他们用ScrapyScrapydRedis分布式跑LinkedIn数据的架构但在国内的网络环境下启不启用分布式爬虫更取决于你的代理资源是否够用而不只是代码层面能不能跑通。3. 核心实现细节与实操步骤3.1 登录状态获取与Keep-AliveLinkedIn登录态是整个爬虫项目的生命线。最稳妥的方式还是在浏览器里手动登录一次然后用浏览器开发者工具F12从网络面板里复制出关键的Cookie信息。具体来说需要关注以下几个请求头的一整套组合headers { user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36, cookie: li_at...; JSESSIONID...; langv2langzh-cn, csrf-token: ajax:..., x-li-track: ... }其中li_at是LinkedIn登录后的长效会话凭证有效期通常是一到两周JSESSIONID是短期会话配合csrf-token做接口鉴权。我踩过的坑是直接用浏览器复制Cookie虽然方便但是请求头里如果少了accept-language或者x-restli-protocol-version这类标志头LinkedIn的接口偶尔会返回一个403看起来像IP被封其实是请求头不完整。所以建议把整个请求头模板固定下来之后所有的请求都复用同一套配置。Cookie过期是必然的解决方案通常是两个方向一是写一个浏览器自动登录脚本到点后用Playwright重新登录并刷新li_at二是维护多个账号的Cookie池轮询使用。个人建议小规模任务直接手动更新Cookie把这个过程做成一键脚本不值得为它写一套账号风控规避的复杂工具。3.2 搜索“公司员工”的两种路径拿到登录态之后接下来要解决的是“输入公司名怎么找到员工列表”。常见的有两种路径路径一目标公司搜索页通过公司主页的员工Tab翻页。搜索框输入公司名字在结果页筛出官方公司主页点进主页后访问/company/{company_id}/people/这个URL。不同页面的翻页参数通常是page或者start比如翻第2页就是people/?page2。路径二直接通过员工搜索页过滤公司。直接打开/search/results/people/在过滤器中把Current Company设为目标公司。这种方式的URL构造灵活能结合关键词比如只找工程师但容易触发LinkedIn的搜索限制弹窗频率控制不好会被风控很快盯上。从稳定性的角度出发我推荐优先用路径一。公司主页的员工列表页是入口结构相对固定反爬措施也宽松一点员工搜索页适合做精准定向补采不适合作为主流程。核心原则是尽量走在LinkedIn认为“正常用户”会走的路上。3.3 员工列表解析与详情页深挖有了员工列表HTML/JSON之后解析阶段的工作量反而被低估了。如果你是用requests直接请求LinkedIn的API端点返回的JSON里通常有一套类似下面的嵌套结构# 伪代码员工卡片解析 def parse_employee(card): return { name: card.get(fullName, ), title: card.get(headline, ), location: card.get(location, ), linkedin_url: card.get(publicIdentifier, ), }注意LinkedIn的字段名经常随版本变化比如headline有时候是position或者occupation解析前建议先用一次真实返回值做结构探查不要对着旧文档硬写。如果你愿意多花一点请求量提升数据质量可以再对每个publicIdentifier构造/in/{username}/详情页从中提取个人简介、教育经历、完整工作经历等列表页没有的信息。但这里请务必设置独立的请求队列和限速参数普通翻页和详情页深挖的比例我建议控制在一比一以下否则风控触发速度会明显变快。3.4 限速与风控意识说到风控LinkedInSpider这类项目最核心的反爬策略其实就两个字慢、变。“慢”是控制请求间隔。实测下来在没有任何代理的情况下单IP的请求间隔低于5秒半小时内就会被限制搜索低于2秒十分钟内大概率弹验证码。我现在习惯的做法是普通列表页请求间隔8到12秒详情页间隔10到15秒深夜跑数据时偶尔把间隔缩短到4秒但不会持续太久。“变”是让请求节奏看起来像真人。每跑完一个公司停40到60秒请求时间固定在某些时间段比如工作日的白天、晚上八点到十一点避开凌晨三点这种明显的机器活跃期。另外一定要给整个爬虫套上随机UA池虽然LinkedIn对UA的检测没有Google那么严格但总是同一个UA也会被挂上低信任标签。如果你有条件上代理池住宅代理那稳定性会好很多——每个IP控制在50到100次请求内就要换。如果没条件那就老老实实把单IP的日请求量压在几百次以内跑完一轮隔几天再跑下一轮。爬虫界的铁律是数据没拿到顶多是效率低IP被风控拉黑才是真正的全军覆没。4. 常见问题与排查技巧实录4.1 登录态校验过期的各种信号LinkedIn的登录过期不会直接报错“登录失败”它通常会给你一些模糊的反馈。最常见的几种“信号”是搜索请求返回的HTML/JSON里没有任何员工数据只有登录引导页或“Join now”按钮。接口返回401 Unauthorized或403 Forbidden但你的UA和Cookie看起来没问题。详情页请求返回正常的HTTP 200但解析出来的字段全部为空。遇到以上情况我的排查顺序是先确认Cookie里的li_at是否真的有效打开浏览器无痕窗口粘进Cookie手动访问一次再检查请求头里的csrf-token是否对应最新的JSESSIONID最后排除是不是公司名输入有误。不要一上来就怀疑IP被封Cookie问题在LinkedIn爬虫里大概占了一半以上。4.2 验证码触发后怎么办验证码captcha是所有爬虫公敌LinkedIn的验证码触发有几个规律可循新登录的账号前几次操作触发概率高短时间高频搜索触发概率高数据中心大城市的IP段触发概率也偏高。第一次触发验证码时不要急着写代码绕过先把浏览器打开手动过掉验证码。这能帮你搞清楚一个问题到底是账号触发了安全验证还是整个IP被标记了。如果是账号级接下来降低请求频率、换一个时间段继续跑即可如果是IP级那就要换出口IP或者歇一段时间短则半小时长则一天。这里的关键心得是触发验证码不是一个点上的问题而是一条线上的问题只有把频率、UA、入口路径全部调温和验证码才会消失。4.3 同名公司与名字匹配不准公司名搜不准是这类“以名找人”项目非常现实的痛点。比如你输入“腾讯”LinkedIn上可能存在“Tencent”“腾讯”“Tencent Games”“Tencent Cloud”等多个主页搜索结果甚至可能把“南京腾讯科技”这种关联公司排在前头。解决方案分两层第一层是在搜索结果列表做精准匹配比对公司主页的名称标准化形式不同语言下用官方英文名优先第二层是把爬取结果的公司归一到你输入时的原始公司名上避免后续数据统计时出现重复和分裂。我个人的做法是维护一张公司别名映射表输入一个公司名程序自动去查它对应的LinkedIn公司ID后续所有请求都用这个ID而不是重新搜索。这样既准确又省搜索次数。4.4 数据量大之后的存储性能问题当你从几十个公司扩张到几百个公司每天新增几万条员工数据之后CSV开始变得难用。同一个员工在不同公司下重复出现、同一公司名在不同语言下重复抓取这些去重逻辑用SQL去重比用Python内存集合适用得多。我是在跑到第40个公司左右时彻底搬进SQLite的。建一张employees表用(company_id, linkedin_url)作为唯一索引后续抓到的数据先INSERT OR IGNORE再更新代码逻辑一下简单了许多。如果未来数据量继续增长可以直接把SQLite换成Postgres表结构几乎不用大改。5. 合规边界与项目延展建议5.1 法律与协议的红线LinkedIn的用户协议明确禁止未经许可的自动化数据采集而“根据公司名抓员工信息”这个动作天然就落在个人数据处理的敏感区。国内《个人信息保护法》施行之后抓取个人公开信息并用于自动化分析在合规上需要更谨慎地评估使用场景和数据的用途。这不是劝退而是提醒爬虫项目本身是中性的工具但使用目的决定了它是否越界。比如用于学术研究、市场宏观分析、公开信息的聚合对比风险相对低但如果把抓到的员工信息用于营销骚扰、人才猎头之外的商业转售那民事责任甚至刑事风险都可能被放大。每一个把LinkedInSpider用起来的人都应该先想清楚自己的使用边界。5.2 从爬虫工具到数据产品的三步延伸跑通LinkedInSpider只是一个起点我个人觉得它真正的价值在于后续的数据加工能力。建议做完基础爬虫后按这三个方向逐步迭代数据清洗增强把原始员工信息接进大模型做结构化抽取比如从职位头衔里提取职级、职能方向从简介里提取技能标签这能让数据从“可读”变成“可分析”。趋势与动态监控定时重跑同一批公司对比数据变化就能得出半导体行业最近三个月的人员流动趋势。这种动态数据比静态名单值钱得多。多平台数据融合把LinkedIn的员工数据与GitHub、技术社区的个人主页关联形成更完整的开发者画像这套画像用在开源社区的贡献者招募上效果好得出奇。5.3 最后再分享两个小技巧第一个是关于公司搜索失败的兜底逻辑当你输入的公司名在LinkedIn上找不到直接匹配时不要直接标记失败可以尝试换用其他语言写法、简称甚至股票代码去搜索。我遇到过好几个公司中文名搜不到但输入英文缩写一下子就出来了。第二个是关于数据字段里最容易被忽略的profile_url务必把它保留好。这个字段不仅仅是去重用的它还是后续做关联分析的钥匙——比如你要对某位候选人做背景调查有了这个url后续所有补充信息都可以围绕它展开。我见过太多人清理字段时把它删了后来追悔莫及。LinkedInSpider这类项目的开发过程很像是在“获取数据的欲望”和“平台的反爬防线”之间走钢丝。技术本身不难难的是对风控的敬畏、对数据质量的偏执以及对合规边界的清醒认知。希望这篇拆解能帮正要踏入这个领域的你少踩几个坑。本文还有配套的精品资源点击获取
返回列表