Как парсить вакансии на Indeed: структура, блоки и маршрут через SERP
Indeed — это крупнейшая доска объявлений о вакансиях в интернете и актуальный сигнал о состоянии рынка труда. Она также защищена Cloudflare с именами классов, которые меняются каждую неделю. Вот как извлекать данные надежно — и когда стоит полностью избегать этого сайта.
Indeed привлекает около 542 миллионов посещений в месяц, что делает его списки одним из самых чистых актуальных сигналов для рынка труда — спрос на вакансии по ролям, диапазоны зарплат, какие компании нанимают сотрудников. Это также защищенный Cloudflare сайт на React, чьи видимые имена CSS-классов меняются достаточно часто, чтобы сломать наивный парсер за несколько недель. Это руководство показывает, как надежно парсить вакансии на Indeed, как осуществлять пагинацию и дедупликацию, реальность Cloudflare и вертикаль jobs SERP, которая позволяет полностью избежать борьбы.
Читайте страницу поиска, опирайтесь на стабильные атрибуты
Единица данных Indeed — это страница результатов поиска: запрос плюс местоположение дают вам страницу с карточками вакансий. Ловушка заключается в выборе по хэшированным именам классов (таким как eu4oa1w0), которые Indeed регенерирует. Вместо этого опирайтесь на атрибуты data-testid, которые меняются гораздо реже:
import requests
from bs4 import BeautifulSoup
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
}
proxies = {"http": "http://USER:PASS@gate.quantumproxies.io:8000",
"https": "http://USER:PASS@gate.quantumproxies.io:8000"}
url = "https://www.indeed.com/jobs?q=python&l=New+York%2C+NY"
html = requests.get(url, headers=headers, proxies=proxies, timeout=30).text
soup = BeautifulSoup(html, "html.parser")
jobs = []
for card in soup.select("div.job_seen_beacon"):
title = card.find("a", class_="jcs-JobTitle")
company = card.find("span", attrs={"data-testid": "company-name"})
location = card.find("div", attrs={"data-testid": "text-location"})
jobs.append({
"title": title.get_text(strip=True) if title else None,
"company": company.get_text(strip=True) if company else None,
"location": location.get_text(strip=True) if location else None,
"jk": title["href"].split("jk=")[-1][:16] if title else None,
})
print(len(jobs), "jobs on page 1")
Каждая карточка содержит ключ вакансии (jk) — стабильный идентификатор, который вы используете для дедупликации между страницами и запусками. Храните по jk, а не по URL, потому что одно и то же объявление появляется с разными параметрами отслеживания.
Пагинация и сайты стран
Indeed использует параметр start, который увеличивается на 10. И это не один сайт — каждая страна имеет свой поддомен: www.indeed.com для США, uk.indeed.com для Великобритании и так далее. Соответствуйте выход вашего прокси стране, которую вы запрашиваете, чтобы запрос был географически согласован:
def crawl(query, location, country="www", pages=5):
base = f"https://{country}.indeed.com/jobs"
for start in range(0, pages * 10, 10):
params = {"q": query, "l": location, "start": start}
html = requests.get(base, params=params, headers=headers,
proxies=proxies, timeout=30).text
# parse as above, yield rows
yield html
# freshness filter: fromage accepts only 1, 3, 7 or 14 days
# .../jobs?q=python&l=NYC&fromage=7
Одна особенность, которую стоит знать: фильтр Indeed "опубликовано в течение" (fromage) принимает только 1, 3, 7 или 14 дней — другие значения игнорируются, поэтому стройте вашу логику свежести вокруг этих диапазонов.

Реальность Cloudflare
Indeed находится за Cloudflare, поэтому IP-адрес центра обработки данных с простым запросом часто получает вызов вместо результатов. Исправления — это обычный антибот-стек: согласованный User-Agent браузера и набор заголовков, и — самый большой рычаг — резидентный выход, который не оценен заранее как автоматизация. Резидентный прокси представляется как соединение реального пользователя, что позволяет Cloudflare обслуживать страницу. Когда появляется вызов, соответствие отпечатка браузера важно так же, как и IP; наше руководство по обходу Cloudflare в 2026 году охватывает, где проходит граница.
Кратчайший путь: вертикаль jobs SERP
Если вам действительно нужны данные о рынке труда — а не конкретно HTML Indeed — есть более чистый путь. Вертикаль вакансий Google агрегирует объявления с Indeed и многих других досок, и SERP API возвращает эту вертикаль вакансий в виде разобранного JSON: название, компания, местоположение, дата публикации и источник, без необходимости поддерживать CSS-селекторы и без борьбы с Cloudflare. Вы обмениваете некоторый контроль на уровне полей на стабильную структурированную ленту сразу с многих досок, что для агрегированных аналитик найма обычно является лучшей сделкой. Это та же логика, что и при создании агрегатора досок вакансий на основе ленты SERP, а не одного парсера на сайт.
Получите вертикаль вакансий как чистый JSON
Для чего полезны данные
Помимо поиска работы, эти данные позволяют проводить реальный анализ: бенчмаркинг зарплат по ролям и городам, отслеживание спроса на конкретные навыки с течением времени, сигналы о найме конкурентов (конкурент, набирающий новую команду, — это стратегический признак) и обогащение — сочетание открытых вакансий компании с фирмографией для исходящих контактов. Соедините это с данными о зарплатах Glassdoor, и вы получите более полную картину компенсаций, чем из любого источника по отдельности.

Часто задаваемые вопросы
Можно ли легально парсить вакансии на Indeed?
Вакансии являются публичными данными, и сбор общедоступных списков в целом защищаем, но условия Indeed ограничивают автоматизированный доступ, и вы должны избегать персональных данных и соблюдать ограничения по скорости. Многие команды обходят вопрос условий, используя агрегированную вертикаль jobs SERP вместо прямого парсинга Indeed. Это общая информация, а не юридическая консультация — изучите текущие условия перед началом большого проекта.
Почему мой парсер Indeed постоянно ломается?
Две причины. Во-первых, Indeed меняет свои хэшированные имена CSS-классов, поэтому селекторы, закрепленные на них, выходят из строя за несколько недель — опирайтесь на атрибуты data-testid и ключ вакансии. Во-вторых, Cloudflare начинает бросать вызовы вашему IP, как только он помечен; резидентный выход и согласованный отпечаток браузера позволяют загружать реальную страницу. Исправьте оба, и парсер стабилизируется.
Как парсить вакансии из нескольких стран?
Indeed обслуживает каждую страну через свой поддомен (www.indeed.com, uk.indeed.com, de.indeed.com и так далее). Запрашивайте правильный поддомен и направляйте через прокси-выход в этой стране, чтобы запрос был географически согласован — иначе вы можете получить несоответствующие или перенаправленные результаты. Вертикаль jobs SERP также принимает параметр страны, если вы не хотите управлять поддоменами.
Является ли Google jobs SERP лучше, чем парсинг Indeed?
Для агрегированных аналитик рынка труда, обычно да. Вертикаль вакансий возвращает объявления с Indeed и многих других досок как структурированный JSON, без вращающихся селекторов и без необходимости справляться с вызовами Cloudflare. Вы теряете некоторые поля, специфичные для Indeed, но получаете стабильную многопотоковую ленту и значительно меньше обслуживания. Парсите Indeed напрямую только тогда, когда вам нужны поля, которые находятся на собственных страницах Indeed.
Indeed вознаграждает надежный подход: опирайтесь на стабильные атрибуты, дедуплицируйте по ключу вакансии, пагинируйте по десяткам и используйте резидентный IP, чтобы Cloudflare обслуживал вас. И когда вам нужен только сигнал рынка труда, а не точный HTML Indeed, вертикаль jobs SERP предоставляет его вам в виде JSON. Выберите путь, который соответствует тому, насколько вам нужен контроль над полями.