Где-то к моей пятидесятой копипасте названия вакансии с Indeed в таблицу я начал всерьёз сомневаться в выборе профессии. Если вы когда-нибудь пытались программно вытащить структурированные данные с Indeed, вы уже знаете финал: ошибка 403 — это не баг, а часть системы защиты Indeed.
Indeed — крупнейшая в мире площадка для поиска работы: около 350 миллионов уникальных посетителей в месяц, 130 миллионов вакансий в любой момент времени и работа более чем в 60 странах. Это делает Indeed одним из самых богатых источников данных о рынке труда на планете — и одновременно одним из самых сложных для парсинга. Open-source парсер JobFunnel (тысячи звёзд на GitHub) в декабре 2025 года был буквально архивирован его автором после многих лет проигранной гонки вооружений с антиботами. Слова самого автора: «Все пользователи могут собрать некоторую часть вакансий, но довольно быстро натыкаются на капчу, и парсинг ломается, не выдавая ни одной вакансии». Другой участник сообщил, что капча появилась уже на первом запросе. Так что да — задача тут совсем не простая. В этом руководстве я разберу все практические способы парсинга Indeed с помощью Python, покажу, как реально пройти через 403-барьер, и — для тех, кто не хочет возиться с отладкой — покажу no-code альтернативу на Thunderbit.
Что значит парсить Indeed с помощью Python?
По сути, веб-скрейпинг — это автоматизированное извлечение структурированных данных с веб-страниц. Когда мы говорим о парсинге Indeed на Python, речь идёт о скрипте, который открывает страницы поиска и карточки вакансий Indeed, читает HTML под капотом (или встроенные данные) и вытаскивает такие поля, как название вакансии, компания, локация, зарплата и описание, в удобный формат — CSV, базу данных или Google Таблицу.
Обычно для этого используют Requests (для HTTP-запросов), BeautifulSoup (для парсинга HTML) и Selenium или Playwright (для автоматизации браузера). Но Indeed — не простой статический сайт. Это гибрид: HTML, рендерящийся на сервере, плюс встроенный JSON-blob со состоянием страницы, а сверху ещё и Cloudflare Bot Management. Это значит, что парсеру нужно уметь работать с контентом, который рендерится JavaScript'ом, с постоянно меняющимися CSS-классами и с агрессивной антибот-защитой — ещё до того, как вы разберёте хоть одно название вакансии.
И в 2026 году у Indeed по-прежнему нет официального бесплатного API только для чтения. Старый Publisher Jobs API был закрыт примерно в 2020 году, а то, что осталось, доступно только работодателям (Job Sync, Sponsored Jobs). Так что на практике остаются лишь два варианта: скрейпинг или покупка данных у стороннего провайдера.
Зачем парсить данные о вакансиях Indeed?
Бизнес-смысл здесь очевиден: вручную просматривать тысячи объявлений неудобно, а данные внутри них действительно ценны.

| Сценарий использования | Кому полезно | Пример |
|---|---|---|
| Генерация лидов | Отделы продаж и рекрутинга | Составление списков нанимающих компаний с контактами |
| Исследование рынка труда | Аналитики, HR-команды | Выявление востребованных навыков и зарплатных ориентиров по регионам |
| Анализ конкурентов | Работодатели, кадровые агентства | Мониторинг найма конкурентов и их зарплатных предложений |
| Автоматизация личного поиска работы | Соискатели | Сбор вакансий, подходящих под ваши критерии, из разных локаций |
| Обучающие данные для ML-моделей | Дата-сайентисты | Построение моделей прогнозирования зарплат на исторических данных |
Собственные исследования Indeed Hiring Lab подтверждают, что данные о вакансиях тесно коррелируют с показателями BLS JOLTS и могут служить почти реальным индикатором состояния рынка труда США. Хедж-фонды используют динамику публикации вакансий как альтернативный сигнал данных. HR-команды сравнивают компенсации, опираясь на вытащенные диапазоны зарплат. А рекрутеры формируют списки потенциальных клиентов из компаний, которые активно нанимают.
Практическая оговорка: данные по зарплатам на Indeed улучшаются, но всё ещё неполные. По состоянию на середину 2025 года примерно 59% вакансий в США содержат информацию о зарплате, но лишь около 22% указывают точное значение — в остальных случаях это диапазоны. Любой анализ зарплат на основе данных Indeed нужно строить с учётом этой неполноты.
Как выбрать способ парсинга Indeed на Python
Единственно правильного способа нет. Лучший вариант зависит от вашего уровня, объёма данных и того, сколько времени вы готовы тратить на поддержку. Я протестировал все четыре основных подхода, и вот как они выглядят в сравнении:
| Критерий | BS4 + Requests | Selenium | Скрытый JSON (window.mosaic) | No-code (Thunderbit) |
|---|---|---|---|---|
| Сложность | Начальный уровень | Средний | Средний–продвинутый | Нет (2 клика) |
| Скорость | Быстро | Медленно (рендер браузера) | Быстро | Быстро (облачный скрейпинг) |
| Контент, рендерящийся JS | Нет | Да | Да (встроенные данные) | Да |
| Устойчивость к антиботам | Низкая | Средняя (обнаруживается) | Средне-высокая | Высокая (обрабатывается автоматически) |
| Поддержка при изменении HTML | Высокая нагрузка (ломаются селекторы) | Высокая | Средняя (структура JSON стабильнее) | Не требуется (AI адаптируется) |
| Лучше всего подходит для | Быстрых прототипов | Динамических страниц, контента за логином | Массовых структурированных данных | Непрограммистов, быстрого результата |
Это руководство проходит по каждому методу. Если вы Python-разработчик, вам стоит прочитать разделы про BS4, скрытый JSON и Selenium. Если вы не пишете код — или просто устали от отладки 403 — переходите сразу к разделу про Thunderbit.
Перед началом
- Сложность: от начального до среднего уровня (разделы про Python); нет (раздел про Thunderbit)
- Время: примерно 20–60 минут на настройку Python и первый парсинг; около 2 минут с Thunderbit
- Что понадобится: Python 3.9+, редактор кода, браузер Chrome и (для no-code варианта) расширение Thunderbit для Chrome
Настройка Python-окружения для парсинга Indeed
Перед тем как писать код для парсинга, подготовьте окружение.
Установка нужных библиотек
Создайте виртуальное окружение и установите пакеты, которые понадобятся:
python -m venv indeed_env
source indeed_env/bin/activate # В Windows: indeed_env\Scripts\activate
# Для HTTP + парсинга
pip install requests beautifulsoup4 lxml httpx
# Для подхода со скрытым JSON (рекомендуется)
pip install curl_cffi parsel tenacity
# Для автоматизации браузера
pip install selenium
Несколько замечаний:
curl_cffi— стандарт де-факто 2026 года для парсинга сайтов, защищённых Cloudflare. Он имитирует реальные TLS-отпечатки браузеров, чего обычныеrequestsиhttpxне умеют. Ниже в разделе про антиботов я объясню, почему это важно.- Selenium 4.6+ уже включает Selenium Manager, так что ChromeDriver больше не нужно скачивать вручную — управление бинарником браузера происходит автоматически.
- В качестве парсера для BeautifulSoup используйте
lxml. Он примерно в 1,5 раза быстрее, чем стандартныйhtml.parser.
Создание структуры проекта
Сделайте всё просто:
indeed_scraper/
├── scraper.py
├── requirements.txt
└── output/
Все примеры кода ниже опираются на scraper.py.
Как парсить Indeed с помощью Python и BeautifulSoup
Это самый дружелюбный к новичкам способ: используем requests, чтобы получить страницу, и BeautifulSoup, чтобы разобрать HTML. Настроить его проще всего, но на Indeed он же и самый хрупкий.
Шаг 1: Соберите URL поиска Indeed
URL поиска Indeed устроены по предсказуемому шаблону:
https://www.indeed.com/jobs?q=<query>&l=<location>&start=<offset>
Например, поиск вакансий «data analyst» в «Austin, TX» с первой страницы:
from urllib.parse import urlencode
params = {
"q": "data analyst",
"l": "Austin, TX",
"start": 0,
}
url = f"https://www.indeed.com/jobs?{urlencode(params)}"
print(url)
# https://www.indeed.com/jobs?q=data+analyst&l=Austin%2C+TX&start=0
Indeed выводит результаты с шагом 10 и жёстким потолком в 1000 результатов (start <= 990). Любое значение выше 990 молча вернёт ту же самую страницу.
Шаг 2: Отправьте HTTP-запрос с правильными заголовками
Indeed сразу блокирует запросы с дефолтным Python user-agent. Нужны реалистичные заголовки:
import requests
headers = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"
),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Referer": "https://www.indeed.com/",
}
response = requests.get(url, headers=headers, timeout=30)
print(response.status_code)
Если вы получили 200 — пока всё хорошо. Если 403 — Cloudflare вас вычислил. Ниже покажу, как с этим жить.
Шаг 3: Разберите вакансии из HTML
Используйте BeautifulSoup, чтобы выбрать элементы карточек вакансий. Ориентируйтесь на атрибуты data-testid — они стабильнее, чем случайные CSS-классы Indeed:
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text, "lxml")
cards = soup.find_all("div", attrs={"data-testid": "slider_item"})
jobs = []
for card in cards:
title_el = card.find("h2", class_="jobTitle")
title = title_el.get_text(strip=True) if title_el else None
company = card.find(attrs={"data-testid": "company-name"})
location = card.find(attrs={"data-testid": "text-location"})
link = title_el.find("a")["href"] if title_el and title_el.find("a") else None
jobs.append({
"title": title,
"company": company.get_text(strip=True) if company else None,
"location": location.get_text(strip=True) if location else None,
"url": f"https://www.indeed.com{link}" if link else None,
})
print(f"Найдено вакансий: {len(jobs)}")
Шаг 4: Обрабатывайте пагинацию
Проходите по страницам, увеличивая параметр start:
import time, random
all_jobs = []
for page in range(0, 50, 10): # Первые 5 страниц
params["start"] = page
url = f"https://www.indeed.com/jobs?{urlencode(params)}"
response = requests.get(url, headers=headers, timeout=30)
# ... разбираем как выше ...
all_jobs.extend(jobs)
time.sleep(random.uniform(3, 6))
Ограничения этого подхода
Скажу прямо: BS4 + Requests — самый слабый метод для Indeed в 2026 году. Обычный requests использует TLS-библиотеку стандартной библиотеки Python, которая создаёт JA3-отпечаток, мгновенно распознаваемый Cloudflare как «не браузер». Кроме того, он не поддерживает HTTP/2, который Indeed использует. Скорее всего, вас заблокируют уже через несколько страниц. А CSS-селекторы? Indeed регулярно меняет классы вроде css-1m4cuuf и jobsearch-JobComponent-embeddedBody-1n0gh5s очень часто — так что любой селектор по ним это мина замедленного действия.
Используйте этот метод для быстрых прототипов на одной странице. Для чего-то масштабного лучше переходить к скрытому JSON.
Как парсить Indeed с помощью Python через скрытые JSON-данные
Это метод, который я рекомендую большинству Python-разработчиков. Вместо разбора хрупкого HTML вы извлекаете структурированные данные из переменной JavaScript, встроенной в исходный код страницы Indeed: window.mosaic.providerData["mosaic-provider-jobcards"].
В этом JSON-blob уже есть всё, что вам нужно: название вакансии, компания, локация, зарплата, ключ вакансии, дата публикации, флаг удалёнки. Исполнять JavaScript не требуется. Схема стабильна как минимум с 2023 года, поэтому она гораздо устойчивее, чем DOM-селекторы.
Шаг 1: Получите HTML страницы
Используйте curl_cffi вместо requests — он имитирует реальные TLS-отпечатки браузера, а это критично для прохождения Cloudflare:
from curl_cffi import requests as cffi_requests
response = cffi_requests.get(
"https://www.indeed.com/jobs?q=python+developer&l=Remote&start=0",
impersonate="chrome124",
headers={
"Accept-Language": "en-US,en;q=0.9",
"Referer": "https://www.indeed.com/",
},
timeout=30,
)
print(response.status_code, len(response.text))
Почему именно curl_cffi? Это Python-обёртка над curl-impersonate, которая воспроизводит точный TLS ClientHello, HTTP/2 SETTINGS frame и порядок заголовков настоящих браузеров. Это единственный активно поддерживаемый HTTP-клиент на Python, который одной командой обходит и JA3/JA4, и Akamai H2 fingerprint. Среди поддерживаемых вариантов имитации есть chrome120, chrome124, chrome131, а также Safari и Edge.
Шаг 2: Извлеките JSON с помощью регулярного выражения
JSON-blob встроен в тег <script>. Достаньте его через regex:
import re, json
MOSAIC_RE = re.compile(
r'window\.mosaic\.providerData\["mosaic-provider-jobcards"\]=(\{.+?\});',
re.DOTALL,
)
match = MOSAIC_RE.search(response.text)
if match:
data = json.loads(match.group(1))
results = data["metaData"]["mosaicProviderJobCardsModel"]["results"]
print(f"Найдено вакансий в скрытом JSON: {len(results)}")
else:
print("Скрытый JSON не найден — возможно, блокировка или изменение страницы")
Шаг 3: Разберите поля вакансий из JSON
Каждый элемент в results содержит больше данных, чем видно на странице:
jobs = []
for job in results:
jobs.append({
"jobkey": job["jobkey"],
"title": job["title"],
"company": job.get("company"),
"location": job.get("formattedLocation"),
"remote": job.get("remoteLocation"),
"salary": (job.get("salarySnippet") or {}).get("text"),
"posted": job.get("formattedRelativeTime"),
"job_type": job.get("jobTypes"),
"easy_apply": job.get("indeedApplyEnabled"),
"url": f"https://www.indeed.com/viewjob?jk={job['jobkey']}",
})
JSON часто содержит оценки зарплат, атрибуты таксономии (теги навыков) и рейтинги компаний, которых не видно в отрисованном HTML.
Шаг 4: Парсите несколько страниц
Используйте tierSummaries в JSON, чтобы понять общее число результатов, а затем проходите по страницам:
import time, random
all_jobs = []
for start in range(0, 50, 10): # Первые 5 страниц
url = f"https://www.indeed.com/jobs?q=python+developer&l=Remote&start={start}&sort=date"
response = cffi_requests.get(
url,
impersonate="chrome124",
headers={"Accept-Language": "en-US,en;q=0.9", "Referer": "https://www.indeed.com/"},
timeout=30,
)
match = MOSAIC_RE.search(response.text)
if match:
data = json.loads(match.group(1))
results = data["metaData"]["mosaicProviderJobCardsModel"]["results"]
all_jobs.extend([{
"jobkey": j["jobkey"],
"title": j["title"],
"company": j.get("company"),
"location": j.get("formattedLocation"),
"salary": (j.get("salarySnippet") or {}).get("text"),
"url": f"https://www.indeed.com/viewjob?jk={j['jobkey']}",
} for j in results])
time.sleep(random.uniform(3, 7))
print(f"Всего спарсено вакансий: {len(all_jobs)}")
Почему скрытый JSON надёжнее
Структура window.mosaic.providerData меняется гораздо реже, чем CSS-классы. Вы получаете чистые структурированные данные без разбора грязного HTML. При этом антибот-митигировать всё равно нужно — заголовками, задержками и прокси — и об этом ниже.
Как парсить Indeed с помощью Python через Selenium
Selenium — это подход через автоматизацию браузера. Он полезен, когда нужно взаимодействовать со страницей: кликать по панелям вакансий, обрабатывать контент за логином или вытаскивать динамически подгружаемые описания, которых нет в начальном HTML.
Когда использовать Selenium вместо HTTP-клиентов
- Indeed подгружает часть контента динамически (полные описания вакансий в правой панели)
- Нужно парсить страницы, где есть состояние сессии или логин
- Вы делаете небольшой объём парсинга, где скорость не критична
Краткий пример
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
import time
options = Options()
options.add_argument("--disable-blink-features=AutomationControlled")
# options.add_argument("--headless=new") # Headless легче обнаружить — используйте осторожно
driver = webdriver.Chrome(options=options)
driver.get("https://www.indeed.com/jobs?q=data+engineer&l=New+York")
time.sleep(3)
cards = driver.find_elements(By.CSS_SELECTOR, "[data-testid='slider_item']")
for card in cards:
try:
title = card.find_element(By.CSS_SELECTOR, "h2.jobTitle").text
company = card.find_element(By.CSS_SELECTOR, "[data-testid='company-name']").text
location = card.find_element(By.CSS_SELECTOR, "[data-testid='text-location']").text
print(f"{title} | {company} | {location}")
except Exception:
continue
driver.quit()
Ограничения
Selenium медленный — каждая страница требует полноценного рендеринга браузером. Headless Chrome легко обнаруживается антибот-системой Indeed (Cloudflare проверяет navigator.webdriver, строки vendor для WebGL, количество плагинов и многое другое). Даже undetected-chromedriver лишь откладывает обнаружение, но не устраняет его навсегда. И, как и BS4, ваши селекторы начнут ломаться, когда Indeed обновит интерфейс.
Для большинства задач скрытый JSON даёт те же данные быстрее и с меньшими затратами на поддержку. Selenium оставьте для редких случаев, где браузер действительно необходим.
Как избежать ошибок 403 при парсинге Indeed с помощью Python
Это самый важный раздел. Если вы попали сюда из Google после серии неудач, значит, вы по адресу.

Почему Indeed блокирует ваш парсер
Indeed использует Cloudflare Bot Management плюс Cloudflare Turnstile — не DataDome и не PerimeterX. Заголовки ответа это подтверждают: server: cloudflare, cf-ray и cookie __cf_bm, связанная с управлением ботами. Cloudflare анализирует TLS-отпечаток (JA3/JA4), порядок заголовков HTTP/2, паттерны запросов и сигналы поведения браузера. Если что-то выглядит не по-человечески, вы получаете 403, 429, 503 или — самый коварный случай — 200 OK, но вместо данных вакансий вам отдают страницу-челлендж Turnstile.
Ротация User-Agent и заголовков
Один статичный User-Agent — самый быстрый путь в блокировку. Берите их из пула актуальных, реалистичных строк. Важно: поля minor-версии у Chrome зафиксированы на 0.0.0 после User-Agent Reduction — не придумывайте несуществующие значения, иначе антиботы это заметят.
import random
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.3800.97",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 "
"(KHTML, like Gecko) Version/17.4 Safari/605.1.15",
]
headers = {
"User-Agent": random.choice(USER_AGENTS),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Referer": "https://www.indeed.com/",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
}
Убедитесь, что ваши sec-ch-ua Client Hints совпадают с версией UA. Если sec-ch-ua: "Chrome";v="131" идёт рядом с User-Agent, где указан Chrome 145, это мгновенный красный флаг.
Добавляйте случайные задержки между запросами
Фиксированные интервалы легко вычисляются по паттернам. Используйте случайный джиттер:
import time, random
# Между запросами
time.sleep(random.uniform(3, 6))
# Задержка после блока
def backoff_sleep(attempt):
base = 4
sleep_time = base * (2 ** attempt) + random.uniform(0, 2)
time.sleep(min(sleep_time, 60))
Судя по консенсусу в ScrapeOps и WebScraping.AI, безопасный интервал — 3–6 секунд между запросами с одного IP, а после примерно 100 запросов на IP за сессию стоит ротировать адрес.
Используйте ротацию прокси
Это самый важный фактор успеха. Дата-центровые прокси из диапазонов AWS/GCP дают примерно 5–15% успешных запросов против Cloudflare Enterprise — на Indeed это почти бесполезно. Residential-прокси плюс корректный TLS-отпечаток поднимают успех до 80–95%.
PROXIES = [
"http://user:pass@us.residential.example:7777",
"http://user:pass@us.residential.example:7778",
"http://user:pass@us.residential.example:7779",
]
proxy = random.choice(PROXIES)
response = cffi_requests.get(
url,
impersonate="chrome124",
headers=headers,
proxies={"https": proxy},
timeout=30,
)
В 2026 году цены на residential-прокси составляют примерно $4–8,50 за ГБ в зависимости от провайдера и объёма обязательств. Для Indeed лучше начинать с небольшого пула и масштабироваться по мере необходимости.
Корректно обрабатывайте коды 403, 429 и 503
Не делайте слепой повтор. Разные коды означают разное:
def fetch_with_retry(url, proxy_pool, max_retries=5):
for attempt in range(max_retries):
proxy = random.choice(proxy_pool)
headers["User-Agent"] = random.choice(USER_AGENTS)
try:
r = cffi_requests.get(
url,
impersonate=random.choice(["chrome124", "chrome120", "edge101"]),
headers=headers,
proxies={"https": proxy},
timeout=30,
)
# Проверяем коварный случай "200, но с челленджем"
if r.status_code == 200 and "cf-turnstile" not in r.text and "Just a moment" not in r.text:
return r
if r.status_code == 403:
print(f"403 — заблокировано. Меняю прокси, попытка {attempt + 1}")
elif r.status_code == 429:
print(f"429 — слишком много запросов. Снижаю темп.")
elif r.status_code == 503:
print(f"503 — сервер перегружен или JS-челлендж.")
backoff_sleep(attempt)
except Exception as e:
print(f"Ошибка запроса: {e}")
backoff_sleep(attempt)
raise RuntimeError(f"Не удалось после {max_retries} попыток: {url}")
Случай с 200 и челленджем — самый неприятный. Всегда проверяйте тело ответа на cf-turnstile или Just a moment, прежде чем считать ответ успешным.
Более простой путь: пусть Thunderbit возьмёт антибот-защиту на себя
Если вы не хотите собирать и поддерживать пул прокси, крутить заголовки и имитировать TLS-отпечатки, облачный скрейпинг Thunderbit сам обрабатывает капчи, ротацию прокси и антибот-защиту. Без настройки прокси, без конфигурации curl_cffi, без библиотек для решения капчи. Когда нужны просто данные — это путь с наименьшим сопротивлением.
Почему ваш парсер Indeed постоянно ломается (и как это исправить)
Стена 403 — это острая боль. Хроническая боль — поддержка: парсер, который сегодня работает, на следующей неделе ломается и молча возвращает пустые данные или устаревшие результаты.
Как Indeed ломает ваши селекторы
Indeed агрессивно меняет CSS-классы. В руководстве Bright Data прямо говорится, что классы вроде css-1m4cuuf и css-1rqpxry «похоже, генерируются случайно — вероятно, на этапе сборки». A/B-тестирование означает, что разные сессии видят разные макеты страницы. А перестройка DOM происходит без предупреждения.
История JobFunnel очень показательна. Один из участников написал: «CaptchaBuster успешно справился с капчей, а причина, по которой страница всё ещё не парсилась, [заключалась] в устаревших селекторах Beautiful Soup». Парсер не был заблокирован — он просто искал не те элементы.
Стратегия: предпочитайте скрытый JSON DOM-парсингу
Blob window.mosaic.providerData остаётся стабильным по схеме как минимум с 2023 года. Путь metaData.mosaicProviderJobCardsModel.results[] всё ещё канонический в 2026-м. DOM-селекторы ломаются ежемесячно. Извлечение JSON — если вообще ломается — то раз в год.
Стратегия: используйте data-атрибуты вместо CSS-классов
Когда всё же нужно обращаться к DOM, опирайтесь на функциональные атрибуты:
| Селектор | Назначение |
|---|---|
[data-testid="slider_item"] | Контейнер каждой карточки вакансии |
[data-testid="job-title"] или h2.jobTitle > a | Ссылка с названием вакансии |
[data-testid="company-name"] | Название работодателя |
[data-testid="text-location"] | Текст локации |
data-jk="<jobkey>" на каждой карточке | Самый стабильный хук — не меняется с 2019 года |
Добавляйте проверки, чтобы ловить устаревшие селекторы
Никогда не позволяйте парсеру тихо работать с нулевым результатом. Добавляйте проверку после каждого запроса:
results = parse_hidden_json(html)
assert len(results) > 0, (
f"Indeed вернул пустой набор результатов при start={start} — "
"возможна блокировка, CAPTCHA или смена селекторов. "
f"Первые 500 символов ответа: {html[:500]}"
)
При ошибке логируйте первые 500–2000 символов исходного ответа. Так вы сразу поймёте, получили ли вы челлендж Turnstile, стену логина или изменение схемы. В CI стоит запускать ежедневный smoke-test с фиксированным запросом (например, q=python&l=remote) и проверять, что результаты не пустые.
AI-альтернатива: парсеры, которые не ломаются
AI Thunderbit каждый раз заново читает структуру страницы — он не зависит от жёстко прописанных селекторов или regex-шаблонов. Когда Indeed меняет HTML, Thunderbit адаптируется автоматически. Это напрямую снимает ту нагрузку на поддержку, о которой пользователи форумов говорят чаще всего. Если вы хоть раз просыпались от сообщения в Slack вроде «парсер снова вернул пустые строки», вы понимаете цену решения, которое не нужно чинить.
Как парсить Indeed без написания Python: no-code альтернатива
Почти все конкурирующие руководства исходят из того, что вы будете писать код на Python. Но данные с форумов говорят об обратном. Люди пишут: «это просто ужасно сложно из-за постоянных багов и ошибок», а некоторые предлагают нанять кого-то на Fiverr только ради получения данных. Если это про вас — этот раздел может стать выходом.
Как парсить Indeed с помощью Thunderbit (пошагово)
Шаг 1: Установите расширение Thunderbit для Chrome из Chrome Web Store. Начать можно бесплатно.
Шаг 2: Откройте страницу результатов поиска Indeed в браузере — например, https://www.indeed.com/jobs?q=data+analyst&l=Austin%2C+TX.
Шаг 3: Нажмите на иконку Thunderbit в панели браузера, затем нажмите «AI Suggest Fields». AI Thunderbit просканирует страницу и автоматически определит такие столбцы, как Название вакансии, Компания, Локация, Зарплата, URL вакансии и Дата публикации. Вы можете проверить и скорректировать предложенные поля — удалить ненужные столбцы или добавить свои, просто описав, что нужно, обычным английским.
Шаг 4: Нажмите «Scrape». Thunderbit извлечёт данные со страницы и покажет их в структурированной таблице. Вы увидите строки с вакансиями и теми полями, которые настроили.
Обогащение через парсинг подстраниц
После сбора страницы со списком нажмите «Scrape Subpages», чтобы Thunderbit посетил каждую отдельную страницу вакансии. Он заберёт полные описания вакансий, требования, льготы и ссылки на отклик — без дополнительной настройки. По сути, это эквивалент второго Python-парсера, который открывал бы каждый URL вида /viewjob?jk=<jobkey>, только в один клик.
Автоматическая обработка пагинации
Thunderbit сам обрабатывает постраничную навигацию Indeed. Не нужно вручную собирать offset-URL или писать циклы пагинации. Он кликает по страницам и собирает результаты.
Экспорт в любимые инструменты
Экспортируйте данные в CSV, Excel, Google Sheets, Airtable или Notion — абсолютно бесплатно. Не нужно писать код вроде csv.writer() или pandas.to_csv().
Когда выбирать Python, а когда Thunderbit
| Сценарий | Лучший инструмент |
|---|---|
| Собственные data pipeline, автоматизация по расписанию через cron/Airflow | Python |
| Интеграция в большой кодовый проект | Python |
| Сильно кастомизированная логика парсинга | Python |
| Разовое исследование или анализ рынка | Thunderbit |
| Данные нужны членам команды без технического бэкграунда | Thunderbit |
| Нужно получить данные прямо сейчас без отладки 403 | Thunderbit |
| Обогащение подстраниц без настройки | Thunderbit |
Сравнение по времени: настройка Python + отладка антибота = от часов до дней (особенно в первый раз). Thunderbit = меньше 2 минут для тех же данных. Я не говорю, что Python — это плохо; я говорю, что всё зависит от задачи.
Законно ли парсить Indeed? Что нужно знать
Ни один из популярных гайдлайнов по парсингу Indeed не касается законности, хотя вопрос «законно ли парсить Indeed?» регулярно всплывает на форумах. Это не юридическая консультация, но вот общая картина.
Условия использования Indeed
В ToS Indeed (indeed.com/legal) нет общего запрета на «скрейпинг». Единственный явный запрет на автоматизацию находится в разделе A.3.5: там запрещено «использовать любую автоматизацию, скрипты или ботов для автоматизации процесса Indeed Apply». Это узко относится к потоку отклика, а не к пассивному чтению публичных вакансий. Основной инструмент enforcement у Indeed — технический: челленджи Cloudflare, IP-баны, fingerprinting устройства — а не суд.
Важные судебные прецеденты
Самое часто цитируемое дело в США — hiQ Labs v. LinkedIn. В апреле 2022 года 9-й окружной суд постановил, что скрейпинг общедоступных данных «вероятно не нарушает CFAA» (Computer Fraud and Abuse Act). Однако позже hiQ признали виновной в нарушении договора, потому что её сотрудники создавали фальшивые профили LinkedIn и приняли условия использования.
Более позднее дело Meta v. Bright Data (N.D. California, январь 2024) дало ещё более чёткий вывод. Судья Чен решил, что условия Facebook и Instagram «не запрещают скрейпинг публичных данных в режиме без входа в аккаунт». В следующем месяце Meta добровольно отозвала оставшиеся претензии.
robots.txt Indeed
robots.txt Indeed в целом запрещает /jobs/ и /job/ для стандартного User-agent: *, но явно разрешает Googlebot и Bingbot доступ к /viewjob? — отдельным страницам вакансий. AI-краулерам для обучения (GPTBot, CCBot, anthropic-ai) доступ сильно ограничен. robots.txt не является юридически обязательным в США, но его соблюдение — хорошая практика и признак добросовестности.
Практические рекомендации для ответственного скрейпинга
- Собирайте только общедоступные данные — никогда не входите в аккаунт и не создавайте фальшивые профили
- Соблюдайте лимиты: 1 запрос каждые 3–6 секунд с одного IP, без высокой параллельности
- Не перепубликуйте данные как собственную доску вакансий
- Используйте данные для личного или внутреннего анализа, а не для коммерческой перепродажи без разрешения
- Удаляйте или хэшируйте лишние персональные данные; ограничивайте срок хранения чувствительных к личности данных
- Если вы работаете в масштабе или в ЕС/Великобритании, проконсультируйтесь с юристом — на собранные персональные данные распространяются обязанности по прозрачности из статьи 14 GDPR
По уровню риска: автоматизация личного поиска работы — это нижняя граница. Массовая коммерческая перепродажа данных Indeed — верхняя.
Итоги и ключевые выводы
Парсить Indeed на Python можно, но это не тот проект, который вы сделаете за выходные и забудете. Защита Cloudflare, меняющиеся селекторы и агрессивные антибот-механизмы означают, что подходить к задаче нужно с правильными инструментами и ожиданиями.
Вот что я бы вынес из всего этого:
- Indeed — самый богатый источник данных о рынке труда в вебе: 350 млн посетителей в месяц, 130 млн вакансий — но он жёстко сопротивляется парсингу.
- Извлечение скрытого JSON (
window.mosaic.providerData) — самый надёжный подход на Python. Схема стабильна годами, тогда как CSS-селекторы ломаются каждый месяц. curl_cffiс имитацией браузера — HTTP-клиент по умолчанию в 2026 году для сайтов под Cloudflare. Обычныеrequestsиhttpxблокируются уже на уровне TLS-отпечатка.- Всегда используйте ротацию заголовков, случайные задержки и residential-прокси, чтобы избегать ошибок 403. Дата-центровые прокси почти бесполезны против Cloudflare Enterprise.
- Добавляйте assert-проверки, чтобы сразу понимать, когда сломались селекторы или вместо данных вам отдали страницу-челлендж.
- Для нетехнических пользователей или тех, кому нужен быстрый результат, Thunderbit даёт no-code, AI-подход, который автоматически подстраивается под изменения сайта — без прокси, без отладки, без поддержки.
Если хотите попробовать no-code-способ, у Thunderbit есть бесплатный тариф, так что вы можете протестировать его на Indeed без обязательств. А если идёте по пути Python, примеры кода выше — хорошая отправная точка; просто помните, что устойчивость к антиботам нужно считать не второстепенной задачей, а одним из главных требований.
За дополнительными материалами по веб-скрейпингу и инструментам загляните в наши гайды: как парсить сайты на Python, лучшие автоматизированные инструменты веб-скрейпинга и как парсить сайты без блокировок. Ещё вы можете посмотреть уроки на канале Thunderbit в YouTube.
Попробуйте Thunderbit, чтобы быстрее парсить данные Indeed Get Started Free
FAQ
Какие Python-библиотеки лучше всего подходят для парсинга Indeed?
Для HTTP-запросов в 2026 году лучший выбор — curl_cffi: он имитирует реальные TLS-отпечатки браузеров, а это критично для обхода Cloudflare. httpx с HTTP/2 — неплохой запасной вариант для менее защищённых целей. Для парсинга HTML стандартом остаётся BeautifulSoup4 с lxml. Для автоматизации браузера работают Playwright (с playwright-stealth) или undetected-chromedriver, хотя оба всё легче обнаруживаются. Подход со скрытым JSON через regex (window.mosaic.providerData) вообще позволяет обойтись без тяжёлого парсинга.
Почему я постоянно получаю ошибки 403 при парсинге Indeed?
Indeed использует Cloudflare Bot Management, который анализирует TLS-отпечаток (JA3/JA4), порядок заголовков HTTP/2, шаблоны запросов и поведение браузера. Если вы используете обычный requests, ваш TLS-отпечаток сразу выдаёт Python-скрипт — 403 приходит ещё до того, как заголовки вообще будут прочитаны. Исправить это можно переходом на curl_cffi с имитацией браузера, ротацией реалистичных User-Agent, случайными задержками (3–6 секунд) и residential-прокси. Также проверяйте случай «200, но с Turnstile-челленджем»: ищите в теле ответа маркеры cf-turnstile.
Можно ли парсить Indeed без кода?
Да. Инструменты вроде Thunderbit позволяют извлекать вакансии Indeed в несколько кликов — установите расширение Chrome, откройте страницу поиска Indeed, нажмите «AI Suggest Fields», затем «Scrape». AI Thunderbit автоматически определяет поля вроде названия вакансии, компании, локации и зарплаты. Он сам обрабатывает пагинацию, обогащение подстраницами (полные описания вакансий) и антибот-защиту. Экспорт в CSV, Google Sheets, Airtable или Notion — бесплатно.
Как часто Indeed меняет HTML-структуру?
Indeed регулярно меняет CSS-классы (например, css-1m4cuuf, случайные хэш-строки) и перестраивает элементы DOM без предупреждения. A/B-тестирование означает, что разные пользователи могут одновременно видеть разные макеты. Подход со скрытым JSON (window.mosaic.providerData) значительно стабильнее — схема остаётся неизменной как минимум с 2023 года. Если без DOM-селекторов не обойтись, опирайтесь на data-testid и data-jk (ключ вакансии), а не на CSS-классы.
Законно ли парсить Indeed?
Парсинг публично доступных URL Indeed без входа в аккаунт в США вряд ли повлечёт ответственность по CFAA — это следует из решения 9-го округа по делу hiQ v. LinkedIn (2022) и дела Meta v. Bright Data (2024). Условия использования Indeed запрещают именно автоматизацию процесса отклика Apply, а не пассивное чтение публичных вакансий. Тем не менее действуйте ответственно: не входите в аккаунт, не создавайте фальшивые учётные записи, соблюдайте лимиты, не перепубликуйте данные как собственную доску вакансий и аккуратно обращайтесь с персональными данными (имена рекрутеров, email) в рамках GDPR/CCPA. Для коммерческих проектов лучше проконсультироваться с юристом.
Узнать больше


