爬取招聘网站是人力资源分析、劳动市场监测和招聘自动化中最受欢迎的数据收集场景之一。但招聘网站积极防止自动数据收集:在 50-100 个请求后封锁 IP,显示验证码并禁止可疑账户。在本文中,我们将讨论如何正确设置代理,以便稳定地爬取 hh.ru、Superjob、LinkedIn 和其他平台,而不被封锁。
为什么招聘网站会封锁爬虫以及保护机制如何运作
招聘网站因爬虫而损失资金:数据被竞争对手出售,未经授权的聚合器被创建,雇主绕过付费发布。因此,所有大型平台都实施了多层次的自动数据收集保护。
招聘网站的主要保护方法:
- 按 IP 限制请求速率 — hh.ru 在每小时 80-120 个请求后封锁 IP,Superjob 在 50-70 个请求后封锁。封锁时间从 1 小时到 1 天不等。
- 浏览器指纹识别 — 网站分析 User-Agent、HTTP 头、屏幕分辨率和已安装的字体。如果数据与真实浏览器不符,请求将被封锁。
- JavaScript 检查 — 许多网站使用 Cloudflare 或自有脚本检查请求是否来自真实浏览器,而非机器人。
- 蜜罐陷阱 — 隐藏的链接和字段,只有爬虫可以看到。如果机器人访问这些链接,IP 将被列入黑名单。
- 可疑活动时的验证码 — 在快速请求序列后或使用数据中心 IP 时出现。
没有代理,您最多只能爬取 100-200 个招聘信息,之后您的 IP 将被封锁。对于大规模数据收集(每天数千个招聘信息),代理成为必不可少的工具。
重要: 爬虫必须符合网站的使用条款。许多招聘网站提供官方 API,以合法访问数据。例如,hh.ru 提供免费 API,有请求限制,适合大多数任务。
选择哪种类型的代理来爬取招聘信息
选择代理类型取决于爬虫的规模、预算和速度要求。我们将讨论三种主要选项及其具体使用场景。
| 代理类型 | 速度 | 封锁风险 | 何时使用 |
|---|---|---|---|
| 数据中心代理 | 高(50-200 毫秒) | 高 | 爬虫测试,收集无需授权的公共数据 |
| 住宅代理 | 中(200-800 毫秒) | 低 | 大规模爬取 hh.ru、Superjob,带 IP 轮换 |
| 移动代理 | 中(300-1000 毫秒) | 非常低 | 带授权的爬虫,绕过 LinkedIn 的严格保护 |
数据中心代理用于爬虫
这是最快和最便宜的选择,但有一定限制。数据中心 IP 容易被网站识别,因此它们仅适用于简单任务:无需授权的招聘信息列表爬取、公共数据收集、在使用住宅代理之前测试爬虫。
数据中心代理有效的情况:
- 小规模数据爬取(每天不超过 500 个招聘信息)
- 从没有严格保护的网站收集数据(小型地区招聘网站)
- 使用官方 API 进行 IP 轮换以绕过请求速率限制
- 爬取 RSS 源和招聘信息的 XML 文件
对于 hh.ru 和 Superjob,数据中心代理的效果不稳定:在 20-30 个请求后您会收到验证码,而且许多 IP 已经被这些网站列入黑名单。
住宅代理 — 招聘网站的最佳选择
住宅代理使用真实家庭用户的 IP 地址,因此网站将其视为普通访客。这是爬取招聘信息的价格和质量的最佳平衡。
爬取招聘网站的优势:
- 低封锁风险 — hh.ru 和 Superjob 无法区分住宅 IP 和真实用户
- 大量 IP 地址池 — 可以为每个请求设置轮换或每 5-10 分钟轮换一次
- 地理绑定 — 可以使用该地区的 IP 爬取特定城市的招聘信息
- 稳定性 — 一个住宅 IP 可以处理 200-500 个请求而不被封锁
对于大规模爬虫(每天超过 1000 个招聘信息),带 IP 轮换的住宅代理是标准解决方案。您可以设置每 5-10 分钟更换 IP,在请求之间添加随机延迟(3-7 秒),从而实现稳定的数据收集而不被封锁。
移动代理用于 LinkedIn 和带授权的爬虫
移动代理使用移动运营商的 IP。它们的主要优势是一个 IP 同时被数百名真实用户使用,因此网站无法在不封锁成千上万的普通访客的情况下封锁此地址。
何时需要移动代理:
- 爬取 LinkedIn — 该平台对机器人的保护最为严格,并积极封锁数据中心和甚至住宅 IP
- 带授权的工作 — 如果您需要爬取封闭的招聘信息或个人资料数据,移动 IP 降低了账户被封锁的风险
- 爬取国外招聘网站 — Indeed、Glassdoor、Monster 使用先进的保护系统,移动 IP 的效果更可靠
- 绕过严格的封锁 — 如果您的住宅代理开始收到验证码,切换到移动代理可以解决问题
移动代理的缺点是价格高和速度较慢。但对于关键任务,封锁不可接受,这是最佳选择。
hh.ru 爬取的特点:保护和绕过方法
hh.ru 是俄罗斯最大的招聘网站,在国内招聘网站中具有最先进的爬虫保护。该网站使用请求速率限制、指纹识别和行为分析的组合来识别机器人。
hh.ru 的保护机制如何运作
1. 按 IP 地址限制: 从一个 IP 每小时 80-120 个请求后,网站开始显示验证码或返回 HTTP 429(请求过多)。封锁时间根据爬虫的激进程度从 1 到 6 小时不等。
2. 检查 User-Agent 和头部: hh.ru 分析 HTTP 请求的头部。如果 User-Agent 不符合真实浏览器或缺少标准头部(Accept-Language、Accept-Encoding),请求将被封锁。
3. JavaScript 检查: hh.ru 的某些页面需要执行 JavaScript 才能加载数据。简单的 HTTP 爬虫没有无头浏览器无法获取完整内容。
4. 蜜罐链接: 页面上有隐藏元素,只有爬虫可以看到。如果您的脚本访问这些链接,IP 将被列入黑名单 24 小时。
绕过 hh.ru 保护的代理策略
为了在不被封锁的情况下稳定爬取 hh.ru,请使用以下配置:
hh.ru 爬虫的最佳设置:
- 代理类型: 住宅代理,每 5-10 分钟轮换一次 IP
- 请求之间的延迟: 4-8 秒(随机值)
- User-Agent: 现代浏览器(Chrome、Firefox、Safari 最新版本)的真实 User-Agent 轮换
- 头部: 完整的标准浏览器头部(Accept、Accept-Language、Accept-Encoding、Referer)
- Cookies: 在同一会话中保存和传递 cookies
- 请求限制: 每个 IP 不超过 60-80 个请求,之后更换代理
安全操作的示例:
- 连接到来自所需地区的住宅代理(例如,莫斯科)的 IP
- 对 hh.ru 的主页发出第一个请求,获取并保存 cookies
- 等待 5-7 秒(模拟阅读页面)
- 对带有所需过滤器的招聘信息搜索页面发出请求
- 爬取招聘信息列表(通常每页 20-50 条)
- 对每个招聘信息发出请求以获取详细页面,延迟 4-6 秒
- 在 60-70 个请求后更换代理并重复循环
在这种策略下,您可以从一个流中每天爬取 1000-2000 个招聘信息而不被封锁。如果需要更大的量,请启动多个并行流并使用不同的代理。
建议: hh.ru 提供免费 API 以访问招聘信息。对于大多数任务(劳动市场分析、薪资监测),API 将比爬取 HTML 更稳定。可以在使用 API 时使用代理进行 IP 轮换,以绕过请求速率限制。
爬取 Superjob、LinkedIn 和国外平台
Superjob:保护特点
Superjob 的保护相对 hh.ru 较弱,但仍积极对抗爬虫。主要区别如下:
- 较低的请求速率限制: 在每小时 50-70 个请求后会被封锁(相对于 hh.ru 的 80-120)
- 头部检查不那么严格: 可以使用简化的头部集合
- 缺乏 JavaScript 保护: 大多数数据可以通过简单的 HTTP 请求获取,无需无头浏览器
- 区域封锁: 某些招聘信息仅可通过特定区域的 IP 访问
对于 Superjob,使用住宅代理每 10-15 分钟轮换一次,并在请求之间设置 3-5 秒的延迟就足够了。这将使您每天稳定地爬取 500-1000 个招聘信息。
LinkedIn:最严格的保护
LinkedIn 是一个独特的案例。该平台使用先进的机器学习算法来识别机器人,并在所有社交网络和招聘网站中具有最激进的保护系统之一。
LinkedIn 的保护特点:
- 强制授权: 大多数数据仅对已授权用户可用
- 行为分析: LinkedIn 分析行为模式:滚动速度、鼠标移动、在页面上的时间
- 账户封锁: 在可疑活动下,不仅 IP 被封锁,账户也会被封锁
- 个人资料查看限制: 免费账户每月只能查看有限数量的个人资料
- 强制执行 JavaScript: 没有无头浏览器,爬虫无法进行
LinkedIn 爬虫策略:
- 使用移动代理 — 它们提供最低的封锁风险。一个移动 IP 每天可以用于 100-200 次个人资料查看。
- 必须使用无头浏览器 — 使用 Puppeteer 或 Playwright,并设置真实的浏览器指纹(屏幕分辨率、WebGL、Canvas)。
- 缓慢的爬虫速度 — 每个账户每小时不超过 20-30 个个人资料。请求之间添加 10-20 秒的延迟。
- 模拟真实行为 — 滚动页面、随机点击、在个人资料的不同部分之间切换。
- 预热账户 — 新的 LinkedIn 账户不能立即用于爬虫。需要 1-2 周模拟普通用户的活动。
- 账户轮换 — 使用多个账户和不同的代理分配负载。
爬取 LinkedIn 是所有招聘网站中最复杂的任务。如果您需要从该平台获取数据,请考虑使用官方的 Sales Navigator API 或提供合法数据的第三方服务。
国外招聘网站:Indeed、Glassdoor、Monster
国外平台通常具有比俄罗斯网站更严格的保护(hh.ru 除外)。主要特点如下:
- Indeed — 使用 Cloudflare 和 JavaScript 检查。需要无头浏览器和来自您爬取招聘信息国家的住宅/移动代理。
- Glassdoor — 访问大多数数据需要授权。积极封锁数据中心 IP。使用住宅代理和缓慢的爬虫速度(8-12 秒的延迟)。
- Monster — 为合作伙伴提供 API,但爬取 HTML 需要与目标国家地理绑定的住宅代理。
对于所有国外平台,代理的地理绑定至关重要。如果您在美国爬取招聘信息,请使用美国住宅 IP。来自其他国家的请求可能会引起怀疑并导致封锁。
IP 轮换和请求之间的延迟设置
正确设置代理轮换是稳定爬虫而不被封锁的关键。我们将讨论两种主要策略:每个请求轮换和按时间轮换。
每个请求轮换(Rotating Proxies)
在这种方法中,每个 HTTP 请求都使用新的 IP 地址。这是最安全的方法,但也有一些限制:
优点:
- 无法追踪单个 IP 的活动
- 可以在单位时间内发出更多请求
- 无需跟踪每个 IP 的限制
缺点:
- 无法保持会话(cookies 在更换 IP 时丢失)
- 不适合需要授权的爬虫
- 某些网站会封锁请求,如果 IP 更换过于频繁
每个请求轮换适合在没有授权的情况下爬取 hh.ru 和 Superjob 的公共页面。通过代理提供商的参数进行设置(通常是一个自动轮换的特殊端点)。
按时间轮换(Sticky Sessions)
在这种方法中,一个 IP 在特定时间内(5-30 分钟)使用,然后自动更换。这是大多数招聘网站爬虫任务的最佳选择。
推荐的轮换间隔:
| 网站 | 轮换间隔 | 每个 IP 的最大请求数 | 请求之间的延迟 |
|---|---|---|---|
| hh.ru | 5-10 分钟 | 60-80 | 4-8 秒 |
| Superjob | 10-15 分钟 | 50-70 | 3-5 秒 |
| 30-60 分钟 | 20-40 | 10-20 秒 | |
| Indeed | 10-20 分钟 | 40-60 | 5-10 秒 |
| Glassdoor | 15-30 分钟 | 30-50 | 8-12 秒 |
设置随机延迟
在请求之间设置固定延迟(例如,正好 5 秒)对保护系统来说看起来可疑。真实用户不可能如此精确地行动。始终使用随机延迟。
随机延迟的实现示例:
// Python
import time
import random
# 延迟 4 到 8 秒
delay = random.uniform(4, 8)
time.sleep(delay)
# 更复杂的逻辑:有时进行较长的暂停
if random.random() < 0.1: # 10% 的概率
time.sleep(random.uniform(15, 30)) # 模拟用户分心
else:
time.sleep(random.uniform(4, 8))
// JavaScript / Node.js
const sleep = (min, max) => {
const delay = Math.random() * (max - min) + min;
return new Promise(resolve => setTimeout(resolve, delay * 1000));
};
// 使用
await sleep(4, 8); // 延迟 4-8 秒
// 有长暂停的概率
if (Math.random() < 0.1) {
await sleep(15, 30); // 10% 的概率进行长暂停
} else {
await sleep(4, 8);
}
添加随机长暂停(15-30 秒)并设置 5-10% 的概率,使爬虫的行为更像真实用户,可能会分心接电话或处理其他任务。
验证码和其他封锁的处理
即使正确设置了代理和延迟,您也可能会遇到验证码或其他类型的封锁。我们将讨论如何正确应对这些情况。
招聘网站的封锁类型
1. HTTP 429 请求过多 — 最常见的封锁类型。网站明确表示您超过了请求限制。通常在响应头中有 Retry-After,指示可以在多少秒后重试请求。
处理方法: 立即更换代理,并将当前 IP 列入黑名单,时间为 Retry-After 中指示的时间(通常为 1-6 小时)。如果 Retry-After 缺失,将 IP 列入黑名单 2 小时。
2. HTTP 403 禁止访问 — IP 在服务器级别被封锁。这是一种更严重的封锁,可能持续数小时到一天。
处理方法: 更换代理并将 IP 列入长期黑名单(24 小时)。分析日志:可能是您爬取过于激进或在需要住宅代理的地方使用了数据中心 IP。
3. 验证码(CAPTCHA) — 网站显示“我不是机器人”的检查。这意味着您的行为被认为可疑,但 IP 尚未完全被封锁。
处理方法: 有三种选择:
- 更换代理 — 最简单的方法。当前 IP 将被列入黑名单 6-12 小时。
- 自动解决验证码 — 使用 2Captcha、Anti-Captcha、CapSolver 等服务。每 1000 个解决方案的费用为 1-3 美元。
- 手动解决 — 如果爬虫时间不紧急,可以将验证码发送给操作员进行手动解决。
4. Cloudflare 挑战 — JavaScript 检查,要求在浏览器中执行代码。普通的 HTTP 库无法通过此检查。
处理方法: 使用无头浏览器(Puppeteer、Playwright、Selenium)并设置真实的指纹。像 puppeteer-extra-plugin-stealth 这样的库可以帮助绕过无头模式的检测。
验证码解决服务的集成
如果您决定自动解决验证码,以下是与流行服务 2Captcha 的集成示例:
// Python 使用 2captcha-python 库
from twocaptcha import TwoCaptcha
import requests
solver = TwoCaptcha('YOUR_API_KEY')
try:
# 解决 reCAPTCHA v2
result = solver.recaptcha(
sitekey='6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
url='https://hh.ru/search/vacancy',
proxy={
'type': 'HTTPS',
'uri': 'login:password@ip:port'
}
)
# 获取解决方案的令牌
captcha_token = result['code']
# 发送带令牌的请求
response = requests.post(
'https://hh.ru/search/vacancy',
data={
'g-recaptcha-response': captcha_token,
# 其他表单参数
},
proxies={
'http': 'http://login:password@ip:port',
'https': 'http://login:password@ip:port'
}
)
except Exception as e:
print(f'验证码解决错误:{e}')
解决一个验证码需要 10-30 秒,费用约为 0.001-0.003 美元。对于大规模爬虫,这可能很昂贵,因此最好设置爬虫以尽量减少验证码的出现。
监控和警报系统
为了确保爬虫稳定运行,重要的是设置封锁监控和自动警报:
监控内容:
- 成功请求的百分比 — 如果低于 90%,需要检查代理和设置
- 每小时的验证码数量 — 如果超过 5-10,您爬取得太激进
- 代理的平均响应速度 — 如果突然增加,可能是代理过载
- 429/403 错误的数量 — 代理质量和设置正确性的指示
- 被封锁的 IP 列表 — 如果同一个 IP 不断被封锁,请将其排除在池外
设置通知(Telegram、电子邮件、Slack),如果成功请求的百分比低于阈值。这将使您能够快速响应问题,避免浪费爬虫时间。
在流行的爬虫工具中设置代理
我们将讨论如何在最流行的招聘网站爬虫工具中设置代理:Python(requests、Scrapy)、Node.js(axios、Puppeteer)和现成的解决方案。
Python:requests 和 Scrapy
Python 是最受欢迎的爬虫语言,得益于 requests、BeautifulSoup 和 Scrapy 库。
使用 requests 库的示例:
import requests
import random
import time
# 代理列表(从提供商处获取)
PROXIES = [
'http://user:[email protected]:8080',
'http://user:[email protected]:8080',
'http://user:[email protected]:8080'
]
# User-Agent 轮换列表
USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
]
def parse_vacancy(url):
proxy = random.choice(PROXIES)
user_agent = random.choice(USER_AGENTS)
headers = {
'User-Agent': user_agent,
'Accept': 'text/html,application/xhtml+xml',
'Accept-Language': 'ru-RU,ru;q=0.9,en;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive'
}
proxies = {
'http': proxy,
'https': proxy
}
try:
response = requests.get(
url,
headers=headers,
proxies=proxies,
timeout=30
)
if response.status_code == 200:
return response.text
elif response.status_code == 429:
print(f'请求速率限制适用于 {proxy},更换代理')
# 暂时从列表中删除代理
return None
else:
print(f'错误 {response.status_code}')
return None
except Exception as e:
print(f'请求错误:{e}')
return None
# 使用
for i in range(100):
html = parse_vacancy('https://hh.ru/vacancy/123456')
if html:
# 处理数据
pass
# 随机延迟
time.sleep(random.uniform(4, 8))
Scrapy 设置示例:
# settings.py
# 启用代理支持
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110,
'scrapy_rotating_proxies.middlewares.RotatingProxyMiddleware': 610,
'scrapy_rotating_proxies.middlewares.BanDetectionMiddleware': 620,
}
# 代理列表
ROTATING_PROXY_LIST = [
'http://user:[email protected]:8080',
'http://user:[email protected]:8080',
'http://user:[email protected]:8080'
]
# 自动检测封锁
ROTATING_PROXY_BAN_POLICY = 'scrapy_rotating_proxies.policy.BanDetectionPolicy'
# 请求之间的延迟
DOWNLOAD_DELAY = 5
RANDOMIZE_DOWNLOAD_DELAY = True # 随机延迟 ±50%
# User-Agent 轮换
DOWNLOADER_MIDDLEWARES.update({
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
'scrapy_user_agents.middlewares.RandomUserAgentMiddleware': 400,
})
# 最大并发请求数
CONCURRENT_REQUESTS = 4
CONCURRENT_REQUESTS_PER_DOMAIN = 1
Node.js:使用 Puppeteer 设置代理
对于爬取带有 JavaScript 的网站(LinkedIn、Indeed),需要无头浏览器。Puppeteer 是 Node.js 中最流行的解决方案。
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
// 用于绕过无头浏览器检测的插件
puppeteer.use(StealthPlugin());
async function parseWithProxy() {
const proxy = 'http://user:[email protected]:8080';
const browser = await puppeteer.launch({
headless: true,
args: [
`--proxy-server=${proxy}`,
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-blink-features=AutomationControlled'
]
});
const page = await browser.newPage();
// 设置真实的 User-Agent
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
);
// 设置其他爬虫逻辑...