Через несколько десятков запросов Indeed, LinkedIn и Glassdoor вместо списка вакансий показывают reCAPTCHA или Cloudflare Turnstile — это стандартная защита джоб-бордов от массового сбора данных, а не разовый сбой. Ниже — рабочие настройки, которые снижают долю заблокированных запросов, карта того, какая CAPTCHA стоит на каждой площадке, и парсер на Python, который решает её через API CaptchaAI.
Как настроить парсинг джоб-бордов, чтобы он не падал на CAPTCHA
| Практика | Зачем нужна |
|---|---|
| Резидентные прокси с ротацией | Распределяют запросы по разным реальным IP вместо одного адреса |
| Пауза 3–5 секунд между страницами | Снижает нагрузку на сервер площадки и риск упереться в rate-limit |
| Один и тот же User-Agent на всю сессию | Меньше ложных срабатываний защиты внутри одной сессии |
| Принимать cookies сессии | Джоб-борды связывают запросы через cookies — без них каждая страница выглядит новым визитом |
| Случайный порядок запросов к страницам поиска | Меньше похоже на автоматический перебор по шаблону |
| Лимит около 200 страниц в день на домен | Снижает риск временной блокировки IP |
Где на джоб-бордах встречается CAPTCHA
| Платформа | Тип CAPTCHA | Когда появляется | Какие данные доступны |
|---|---|---|---|
| Indeed | reCAPTCHA v2 | Высокая частота запросов | Вакансии, зарплаты |
| Cloudflare Challenge | Обнаружение бота | Вакансии, данные о компаниях | |
| Glassdoor | reCAPTCHA v2 | Обнаружение парсинга | Отзывы, зарплаты, вакансии |
| ZipRecruiter | Cloudflare Turnstile | Автоматизированный доступ | Списки вакансий |
| Monster | reCAPTCHA v2 | Страницы поиска | Списки вакансий |
| CareerBuilder | reCAPTCHA v3 | Вход, поиск | Вакансии, поиск по резюме |
Типичные проблемы при парсинге джоб-бордов
| Проблема | Причина | Что делать |
|---|---|---|
| CAPTCHA на каждом запросе | IP в чёрном списке или превышена частота запросов | Смените IP, увеличьте паузы между запросами |
| Пустая страница результатов | Вместо списка вакансий вернулся экран CAPTCHA | Проверяйте наличие CAPTCHA до парсинга HTML |
| «Подтвердите, что вы не робот» | Сработала защита от ботов | Резидентный прокси + реалистичный User-Agent |
| Данные о зарплате требуют входа | Контент закрыт авторизацией | Реализуйте сессию с аутентификацией |
| Результаты отличаются от браузера | Разное местоположение или cookies | Синхронизируйте Accept-Language и гео прокси |
Парсер вакансий с автоматическим решением CAPTCHA
Ниже — рабочий парсер на Python: перед разбором страницы он проверяет, не подсунул ли сайт reCAPTCHA или Cloudflare Turnstile вместо результатов поиска, получает токен через CaptchaAI и повторяет запрос уже с решённой капчей.
import requests
import time
import re
from bs4 import BeautifulSoup
CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"
def solve_captcha(method, sitekey, pageurl, **kwargs):
data = {
"key": CAPTCHAAI_KEY,
"method": method,
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1,
}
data.update(kwargs)
resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(5)
result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
"key": CAPTCHAAI_KEY, "action": "get",
"id": task_id, "json": 1,
})
r = result.json()
if r["request"] != "CAPCHA_NOT_READY":
return r["request"]
raise TimeoutError("Solve timeout")
class JobBoardScraper:
def __init__(self, proxy=None):
self.session = requests.Session()
if proxy:
self.session.proxies = {"http": proxy, "https": proxy}
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
})
def search_jobs(self, base_url, query, location, pages=5):
"""Search job listings across multiple pages."""
all_jobs = []
for page in range(pages):
url = f"{base_url}/jobs?q={query}&l={location}&start={page * 10}"
resp = self.session.get(url, timeout=30)
# Check for CAPTCHA
if self._has_captcha(resp.text):
resp = self._solve_and_retry(resp.text, url)
if resp.status_code == 200:
jobs = self._parse_listings(resp.text)
all_jobs.extend(jobs)
print(f"Page {page + 1}: {len(jobs)} jobs found")
else:
print(f"Page {page + 1}: Request failed ({resp.status_code})")
time.sleep(3) # Rate limit
return all_jobs
def _has_captcha(self, html):
indicators = [
'data-sitekey=',
'g-recaptcha',
'cf-turnstile',
'captcha-delivery',
]
return any(ind in html.lower() for ind in indicators)
def _solve_and_retry(self, html, url):
# Try reCAPTCHA first
match = re.search(r'data-sitekey="([^"]+)"', html)
if match:
sitekey = match.group(1)
# Detect Turnstile vs reCAPTCHA
if 'cf-turnstile' in html:
token = solve_captcha("turnstile", sitekey, url)
field = "cf-turnstile-response"
else:
token = solve_captcha("userrecaptcha", sitekey, url)
field = "g-recaptcha-response"
return self.session.post(url, data={field: token})
return self.session.get(url)
def _parse_listings(self, html):
soup = BeautifulSoup(html, "html.parser")
jobs = []
for card in soup.select(".job_seen_beacon, .jobsearch-ResultsList > li"):
title_el = card.select_one("h2 a, .jobTitle a")
company_el = card.select_one(".companyName, [data-testid='company-name']")
location_el = card.select_one(".companyLocation, [data-testid='text-location']")
salary_el = card.select_one(".salary-snippet, .estimated-salary")
if title_el:
jobs.append({
"title": title_el.get_text(strip=True),
"company": company_el.get_text(strip=True) if company_el else "",
"location": location_el.get_text(strip=True) if location_el else "",
"salary": salary_el.get_text(strip=True) if salary_el else "",
"url": title_el.get("href", ""),
})
return jobs
# Usage
scraper = JobBoardScraper(
proxy="http://user:[email protected]:5000"
)
jobs = scraper.search_jobs(
base_url="https://jobs.example.com",
query="python developer",
location="New York",
pages=10,
)
print(f"Total jobs collected: {len(jobs)}")
Сбор данных о зарплатах по нескольким городам и вакансиям
Такой подход особенно востребован у HR-аналитиков и рекрутинговых агентств из СНГ, которые готовят бенчмарки зарплат по удалённым западным вакансиям для своих клиентов.
Например, сравнивают ставки Data Engineer в Сан-Франциско, Нью-Йорке, Остине и по вакансиям с меткой Remote, чтобы обосновать вилку оплаты при найме в России, Беларуси или Казахстане. Если в выгрузку попадают не только сами объявления, но и отклики или резюме кандидатов, стоит заранее убедиться, что обработка таких данных соответствует 152-ФЗ «О персональных данных» (для аудитории в РФ) или требованиям GDPR — это отдельный вопрос комплаенса, а не часть самого парсинга.
import csv
def collect_salary_data(titles, locations, output_file):
"""Collect salary data across job titles and locations."""
scraper = JobBoardScraper(
proxy="http://user:[email protected]:5000"
)
results = []
for title in titles:
for location in locations:
try:
jobs = scraper.search_jobs(
"https://jobs.example.com",
title, location, pages=3,
)
salaries = [j["salary"] for j in jobs if j["salary"]]
results.append({
"title": title,
"location": location,
"listings": len(jobs),
"with_salary": len(salaries),
"salary_samples": "; ".join(salaries[:5]),
})
time.sleep(5)
except Exception as e:
results.append({
"title": title,
"location": location,
"error": str(e),
})
with open(output_file, "w", newline="") as f:
writer = csv.DictWriter(
f, fieldnames=["title", "location", "listings",
"with_salary", "salary_samples", "error"],
)
writer.writeheader()
writer.writerows(results)
return results
# Collect salary data for market analysis
collect_salary_data(
titles=["Data Engineer", "ML Engineer", "DevOps Engineer"],
locations=["San Francisco", "New York", "Austin", "Remote"],
output_file="salary_data.csv",
)
Часто задаваемые вопросы
Как часто стоит обновлять данные о вакансиях, чтобы бенчмарк оставался актуальным?
Для зарплатной аналитики хватает повторного обхода раз в 3–7 дней — на большинстве джоб-бордов объявления не обновляются чаще. Более частый обход увеличивает долю запросов, упирающихся в CAPTCHA, но не добавляет полезных данных.
Что делать, если LinkedIn или CareerBuilder возвращают reCAPTCHA v3 с низким score вместо явной проверки?
Низкий score обычно означает, что сессия выглядит нетипично: новый IP без истории, отсутствие cookies, нестандартные заголовки. CaptchaAI решает и вызовы reCAPTCHA v3, но снизить их частоту помогает более «тёплая» сессия — постоянные cookies и один User-Agent на весь проход.
Сколько потоков CaptchaAI нужно, чтобы одновременно обходить несколько джоб-бордов?
Для 3–5 источников с умеренной частотой обычно достаточно тарифа ADVANCE ($90/мес, 50 потоков). Для более крупных пайплайнов с параллельным сбором по нескольким городам подойдёт PREMIUM ($170/мес, 100 потоков).
Какой прокси лучше подходит для парсинга джоб-бордов?
Резидентные прокси с ротацией — оптимальный баланс стоимости и стабильности. IP дата-центров LinkedIn и Glassdoor блокируют быстрее остальных площадок.
Законно ли собирать вакансии с публичных досок объявлений?
Сами объявления обычно являются публичными данными, но в пользовательском соглашении большинства площадок есть пункт против автоматического доступа, и соблюдение варьируется от площадки к площадке. Если в выгрузку попадают персональные данные — например, из откликов кандидатов, — их обработка отдельно регулируется 152-ФЗ или GDPR, и это стоит проверить до старта сбора.
Похожие материалы
- Резидентные прокси для решения CAPTCHA
- Почему браузерная автоматизация падает там, где работает API
- Липкие и ротируемые сессии прокси
Собирайте данные о вакансиях и зарплатах в промышленном масштабе — получите API-ключ CaptchaAI и уберите блокировки CAPTCHA из пайплайна.