如何在不使用Selenium的情况下爬取LinkedIn无限滚动页面?
解决LinkedIn人脉页面无限滚动爬取问题(无需Selenium)
LinkedIn的无限滚动页面确实不会把所有内容塞进初始HTML里,用requests+BeautifulSoup直接爬初始页面自然拿不到全部数据。好在LinkedIn是通过后端API动态加载数据的,我们可以直接调用这些API来获取完整的人脉列表,完全不需要Selenium,非常适合部署成Web服务。
下面是具体的解决方案:
1. 先找到LinkedIn的人脉API端点
首先你需要用浏览器开发者工具抓包,定位加载人脉数据的API:
- 打开LinkedIn人脉页面并登录账号
- 按F12打开开发者工具,切换到「Network」标签
- 滚动页面加载更多人脉,观察出现的XHR/Fetch请求,你会找到类似
https://www.linkedin.com/voyager/api/relationships/connections的请求 - 点击这个请求,查看它的「Request Headers」和「Query Parameters」,这些是后续模拟请求的关键信息
2. 核心思路:模拟API请求+分页获取
LinkedIn的人脉API支持分页,通过start(起始位置)和count(单次加载数量)参数控制数据范围(通常count最大设为100效率最高)。我们只需要循环调用这个API,每次递增start的值,直到返回的数据为空。
关键注意事项
- 认证必须有:LinkedIn的API需要登录状态,最核心的是
li_at这个cookie(可以从浏览器的Cookie列表里找到),没有它API会直接返回401未授权。 - 请求头要模拟浏览器:API会验证请求头里的
User-Agent、csrf-token、X-Li-Lang等字段,直接复制浏览器里的请求头即可。
3. 完整代码示例
import requests import json import time def scrape_linkedin_connections(li_at_cookie): # 初始化会话,保持登录状态 session = requests.Session() # 从浏览器复制的请求头,按需调整 headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', 'X-Li-Lang': 'en_US', 'X-Li-Voyager-Id': 'YOUR_VOYAGER_ID', # 从浏览器请求头复制 'csrf-token': 'YOUR_CSRF_TOKEN', # 从浏览器Cookie或请求头复制 'Accept': 'application/vnd.linkedin.normalized+json+2.1' } # 设置关键认证cookie session.cookies.set('li_at', li_at_cookie) all_connections = [] start = 0 count = 100 # 每次请求最多100条,LinkedIn API的上限 while True: try: # 构造API请求URL api_url = f'https://www.linkedin.com/voyager/api/relationships/connections?count={count}&start={start}' response = session.get(api_url, headers=headers) # 检查请求是否成功 if response.status_code != 200: print(f"请求失败,状态码:{response.status_code},响应内容:{response.text[:200]}") break # 解析JSON响应 data = json.loads(response.text) connection_elements = data.get('elements', []) # 没有更多数据就退出循环 if not connection_elements: print("已获取全部人脉数据") break # 提取每个人脉的信息(字段可根据需求调整) for elem in connection_elements: member = elem.get('member', {}) connection_info = { 'full_name': member.get('fullName'), 'headline': member.get('headline'), 'profile_url': f"https://www.linkedin.com/in/{member.get('publicIdentifier')}" if member.get('publicIdentifier') else None, 'avatar_url': None } # 处理头像链接(LinkedIn的头像格式是拼接的) picture_data = member.get('picture', {}).get('com.linkedin.common.VectorImage', {}) if picture_data.get('rootUrl') and picture_data.get('artifacts'): artifact = picture_data['artifacts'][0] connection_info['avatar_url'] = f"{picture_data['rootUrl']}{artifact['fileIdentifyingUrlPathSegment']}" all_connections.append(connection_info) # 更新分页参数 start += count print(f"已加载 {len(all_connections)} 条人脉数据") # 添加请求间隔,避免触发反爬 time.sleep(2) except Exception as e: print(f"爬取过程中出错:{str(e)}") break return all_connections # 使用示例:替换成你自己的li_at cookie if __name__ == "__main__": my_li_at_cookie = "YOUR_LI_AT_COOKIE_VALUE" connections = scrape_linkedin_connections(my_li_at_cookie) print(f"总共获取到 {len(connections)} 个人脉")
4. 部署Web服务的建议
- 会话管理:因为需要用户提供自己的
li_atcookie,你可以设计一个简单的API接口,接收用户的cookie参数,返回爬取的人脉列表。 - 安全提醒:务必告知用户
li_atcookie是敏感信息,相当于登录凭证,不要随意泄露,且cookie会定期过期,需要重新获取。 - 反爬应对:除了添加请求间隔,还可以随机调整
count的值(比如80-100之间),或者轮换User-Agent,降低被封禁的风险。 - 错误处理:在Web服务中增加更完善的错误捕获,比如返回友好的错误信息给用户,而不是直接抛出异常。
这种方法的优势很明显:不需要启动浏览器,资源占用极低,爬取速度快,完全适合部署成轻量级的Web服务。
内容的提问来源于stack exchange,提问作者Nurdin
相关产品推荐
相关产品推荐

