Такие доски объявлений, как Indeed, LinkedIn и Glassdoor, применяют CAPTCHA, когда обнаруживают шаблоны автоматического доступа. Рекрутинговые платформы, исследователи рынка и инструменты HR-аналитики нуждаются в надежном решении CAPTCHA для сбора данных о списках вакансий в больших масштабах.
CAPTCHA на основных досках объявлений
| Платформа | Тип капчи | Курок | Доступные данные |
|---|---|---|---|
| Действительно | reCAPTCHA v2 | Большой объем запросов | Списки вакансий, зарплаты |
| страница Cloudflare-защиты в staging | Обнаружение ботов | Вакансии, данные компании | |
| Стеклянная дверь | reCAPTCHA v2 | Обнаружение парсинга | Отзывы, зарплаты, вакансии |
| ZipRecruiter | Cloudflare Turnstile | Автоматизированный доступ | Списки вакансий |
| Монстр | reCAPTCHA v2 | Страницы поиска | Списки вакансий |
| КарьераСтроитель | reCAPTCHA v3 | Вход, поиск | Объявления о работе, поиск резюме |
Скребок доски объявлений с обработкой CAPTCHA
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:pass@residential.proxy.com: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)}")
Сбор данных о зарплате
import csv
def collect_salary_data(titles, locations, output_file):
"""Collect salary data across job titles and locations."""
scraper = JobBoardScraper(
proxy="http://user:pass@residential.proxy.com: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",
)
Советы по скрытной настройке досок объявлений
| Техника | Почему это помогает |
|---|---|
| Ротация авторизованный сетевой выход | Распределяет запросы по реальным IP |
| Задержка между страницами 3-5 секунд. | Имитирует человеческую скорость просмотра |
| Согласованный пользовательский агент для каждого сеанса | Избегает несовпадения сигналы браузера |
| Принять файлы cookie | Доски объявлений отслеживают сеансы с помощью файлов cookie |
| Рандомизировать порядок поиска | Избегайте последовательных шаблонов страниц |
| Ограничение до 200 страниц/day на домен | Оставайтесь ниже порогов обнаружения |
Поиск неисправностей
| Проблема | Причина | Исправить |
|---|---|---|
| CAPTCHA при каждом поиске | IP помечен или скорость превышена | Переключите IP, добавьте более длительные задержки |
| Пустая страница результатов | Вместо этого вернулся блок CAPTCHA | Обнаружение CAPTCHA перед анализом |
| «Пожалуйста, подтвердите, что вы человек» | Сработало обнаружение ботов | Используйте авторизованный сетевой выход + реалистичный UA |
| Требуется логин для данных о зарплате | Содержание шлюза платформы | Реализация сеанса с аутентификацией |
| Результаты отличаются от браузера | Местоположение/cookie различия | Сопоставьте Accept-Language и географический прокси |
Часто задаваемые вопросы
Сколько списков вакансий я могу просмотреть в день?
При ротации авторизованный сетевой выход и правильных задержках можно получить 500-2000 страниц на домен без использования постоянных CAPTCHA.
Блокируют ли доски вакансий парсинг?
На большинстве досок по трудоустройству есть условия, препятствующие автоматическому доступу, но их соблюдение различается. CAPTCHA — это их основная защита, с которой справляется CaptchaAI.
Какой тип прокси подходящий всего подходит для досок объявлений?
Ротация авторизованный сетевой выход — подходящий баланс затрат и успеха. IP-адреса центров обработки данных часто блокируются LinkedIn и Glassdoor.
Связанные руководства
- Ротация авторизованный сетевой выход
- Качество прокси влияет на скорость решения
- Прикрепленные и чередующиеся сеансы
- Собирайте масштабные данные о рынке труда —получите ключ CaptchaAIдля автоматического решения CAPTCHA.*