你是不是也有过这种场景:打开 LinkedIn,看到一堆岗位和公司信息,手动复制粘贴一两条还行,一旦要做一份“行业岗位地图”或给内部做监控,立刻变成体力活。
这篇文章用更实用的方式聊聊 LinkedIn 爬虫:如何用 Python 爬虫 把公开职位列表抓下来、怎么找到页面里的关键元素、以及如何让你的 网页抓取 脚本更稳定、更少踩坑(并且不把自己搞到“跑两下就挂”)。
提醒:请只抓取公开可访问的数据,并遵守目标网站的服务条款与当地法律法规;不要抓取需要登录才能看到的隐私内容。
不同目标,技术路线完全不一样。常见的抓取方向大概是:
职位信息:岗位标题、公司名、地点、发布时间等(很多时候是公开页面,适合做入门练习)
内容与评论:用于舆情或话题分析(需要更强的清洗与去噪)
潜在客户线索:个人资料、公司页信息(往往涉及登录与合规边界,别一上来就冲这个)
这篇我们聚焦最“好落地”的:LinkedIn 职位抓取。
LinkedIn 不算友好的目标站点,常见翻车原因有这几类:
内容动态加载:你在浏览器里看到的列表,可能是 JavaScript 运行后才出现的
反爬与风控:请求频繁会触发限流、验证码、甚至直接返回空页面
页面结构常变:今天的 class 名,明天可能就改了,解析逻辑立刻失效
网络不稳定与超时:脚本没做重试与超时控制时,失败率会很高
如果你只是想更快拿到数据、把“请求失败/重试/渲染/稳定性”这些麻烦事外包掉,思路之一是交给专门的抓取服务处理。
👉 用 Crawlbase 把“被封/验证码/重试”这些脏活交给 API
这样你可以把精力放在 数据字段、清洗规则、落库和业务分析 上,而不是每天盯着脚本为什么又空了。
做 网页抓取 时,工具选错了,后面会一直别扭。
Requests + BeautifulSoup(轻量)
适合:页面 HTML 里就能拿到你要的数据(或至少能拿到“第一屏”)
优点:更快、资源占用低、部署简单
缺点:遇到强动态加载就会抓不到
Selenium(浏览器渲染)
适合:数据必须等 JS 渲染后才出现
优点:更接近真实用户访问
缺点:更慢、更吃机器、维护成本更高
如果你的目标是更稳定地做 LinkedIn 网页抓取、并且希望减少环境折腾,也可以考虑走“API 抓取”路线。
👉 想更省心地做 LinkedIn 网页抓取?看看 Crawlbase 的抓取接口
通常它能帮你处理一部分复杂的网络层问题,你的代码更像是在“消费数据”,而不是“对抗网站”。
下面是一个偏“模板化”的写法:你只需要在浏览器里打开职位搜索页,用开发者工具确认元素选择器,然后把选择器替换掉即可。
bash
pip install requests beautifulsoup4
python
import time
import csv
from urllib.parse import urlencode
import requests
from bs4 import BeautifulSoup
HEADERS = {
"User-Agent": (
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
),
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
}
def fetch_linkedin_jobs(keyword: str, location: str = "", limit: int = 20) -> list[dict]:
params = {"keywords": keyword}
if location:
params["location"] = location
url = "https://www.linkedin.com/jobs/search/?" + urlencode(params)
resp = requests.get(url, headers=HEADERS, timeout=15)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
# 注意:LinkedIn 页面结构会变化,请在开发者工具里确认选择器
job_cards = soup.select("ul.jobs-search__results-list li") # 可能需要你调整
results: list[dict] = []
for card in job_cards:
title_el = card.select_one("h3.base-search-card__title")
company_el = card.select_one("h4.base-search-card__subtitle")
if not title_el or not company_el:
continue
results.append(
{
"title": title_el.get_text(strip=True),
"company": company_el.get_text(strip=True),
}
)
if len(results) >= limit:
break
return results
def save_to_csv(rows: list[dict], path: str) -> None:
if not rows:
return
with open(path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=rows[0].keys())
writer.writeheader()
writer.writerows(rows)
if name == "main":
jobs = fetch_linkedin_jobs(keyword="data engineer", location="", limit=20)
save_to_csv(jobs, "linkedin_jobs.csv")
time.sleep(1) # 给网站留点喘息空间
print(f"Saved {len(jobs)} rows.")
看返回的 HTML 是不是你想要的页面:有时会返回提示页/空壳页/验证页
重新确认选择器:尤其是列表容器、标题和公司名的 class,最容易变
如果你发现“HTML 里根本没有职位卡片”,那通常是动态加载导致的:要么上 Selenium 做渲染,要么换一种更省维护的抓取方式。
想让 Python 爬虫更稳定、失败率更低,通常靠这些细节堆出来:
加超时、加重试:别让一次网络抖动把整批任务干碎
控制频率与并发:请求越猛,越容易触发风控;慢一点反而更快拿到结果
做数据落地与去重:别只 print,落 CSV/SQLite/数据库,方便复跑与对账
加日志与监控:记录状态码、空结果比例、解析失败数量,出问题一眼定位
把选择器当“配置”:页面一变你就改配置,不用全项目翻代码
当你要规模化跑任务(比如每天跑、多个关键词跑、多个地区跑),维护成本会明显上升。这个时候,很多人会把网络层复杂度交给更专业的方案处理。
👉 用 Crawlbase 提升 Python 爬虫稳定性:减少失败率与维护成本
你自己继续掌控“抓什么字段、怎么清洗、怎么落库”,但不用天天和不稳定的请求对线。
Q1:LinkedIn 允许抓取吗?
通常平台的服务条款会限制自动化抓取行为。更稳妥的做法是:只处理公开可访问数据、控制访问频率、明确用途合规;涉及个人隐私或登录后的内容,别碰。
Q2:为什么我代码没报错,但结果是空的?
最常见是两种情况:页面是 JS 动态加载(HTML 里没有数据),或者你的选择器过期了。先打印/保存响应 HTML 检查,再回到开发者工具重新定位元素。
Q3:只抓职位信息还需要 Selenium 吗?
不一定。能用 Requests 解决就别急着上 Selenium:更快、更省资源、部署更轻。只有确认“数据必须渲染后才出现”时再上浏览器自动化。
写一个 LinkedIn 爬虫 的关键不在于“第一版能跑”,而在于你能不能把它做成 更稳定、更低维护成本 的工具:抓取逻辑清晰、错误可定位、数据可复用。
你可以先用本文的 Python 爬虫 思路做出一个小脚本,从职位抓取开始;等需求变成批量化、持续化时,再考虑用更省心的方式把网络层复杂度降下来,让 网页抓取 变成一件更可控的事。