Как парсить вакансии с динамическими фильтрами
Извлечение данных с площадок с вакансиями: работа с динамическими фильтрами через API для скрейпинга, рендеринг JavaScript и AI-экстракция.
Команда InfraProxy
21 февраля 2026 г.
Почему динамические фильтры ломают традиционный парсинг
Современные площадки с вакансиями загружают контент через JavaScript. Пользователь выбирает регион, зарплатную вилку или сферу деятельности — и страница обновляется без полной перезагрузки. Вакансии подтягиваются асинхронно через API или перерисовываются на клиенте. Для классического скрейпинга это создаёт несколько проблем:
- Пустой HTML при первом запросе — сервер отдаёт лишь оболочку страницы, список вакансий рендерится в браузере после выполнения скриптов.
- Фильтры требуют взаимодействия — нужно кликнуть по элементам, заполнить поля, подождать обновления списка.
- Бесконечная прокрутка и «Загрузить ещё» — контент подгружается по мере скролла или по нажатию кнопки.
- Меняющаяся структура DOM — вёрстка часто зависит от типа фильтра и состояния загрузки.
Простой HTTP-клиент (requests, httpx без рендеринга) в таком случае получает HTML без вакансий. Для надёжного сбора нужен JavaScript-рендеринг и возможность выполнять действия на странице.
Архитектура решения: js_actions + use_js_render + extract_schema
Специализированные API для скрейпинга позволяют объединить три компонента:
- use_js_render — использование headless-браузера (Playwright) для полной загрузки страницы и выполнения JavaScript.
- js_actions — последовательность действий на странице: клики, скролл, ввод текста, ожидание элементов.
- extract_schema — описание структуры извлекаемых данных (название, компания, зарплата и т.д.) в виде JSON Schema.
Это даёт единый сценарий: загрузка → взаимодействие с фильтрами → ожидание появления данных → извлечение по схеме.
Пример запроса к API
{
"url": "https://example.com/jobs",
"use_js_render": true,
"js_actions": [
{"action": "click", "selector": "[data-filter=region]"},
{"action": "type", "selector": "#region-input", "value": "Москва"},
{"action": "click", "selector": ".apply-filters"},
{"action": "wait", "selector": ".job-list-item", "timeout": 5000},
{"action": "scroll", "selector": ".job-list", "direction": "down", "iterations": 3}
],
"extract_schema": {
"jobs": {
"selector": ".job-list-item",
"type": "list",
"items": {
"title": {"selector": ".job-title"},
"company": {"selector": ".company-name"},
"salary": {"selector": ".salary"}
}
}
}
}
Код на стороне клиента отправляет такой объект в API, получает task_id и опрашивает статус до готовности результата.
Настройка асинхронного запроса и поллинга
API для скрейпинга обычно работает асинхронно: запрос с use_js_render и js_actions создаёт задачу, которая выполняется в очереди. Клиент не ждёт ответа в одном HTTP-запросе, а периодически проверяет статус.
import httpx
import time
API_KEY = "your_api_key"
BASE_URL = "https://api.example.com/v1"
# Отправка задачи
resp = httpx.post(
f"{BASE_URL}/scrape",
json={
"url": "https://example.com/jobs",
"use_js_render": True,
"js_actions": [
{"action": "click", "selector": ".load-more"},
{"action": "wait", "selector": ".job-card", "timeout": 3000}
]
},
headers={"Authorization": f"Bearer {API_KEY}"}
)
task_id = resp.json()["task_id"]
# Поллинг до готовности
while True:
status = httpx.get(f"{BASE_URL}/tasks/{task_id}", headers={"Authorization": f"Bearer {API_KEY}"}).json()
if status["state"] == "completed":
data = status["result"]
break
if status["state"] == "failed":
raise Exception(status.get("error", "Unknown error"))
time.sleep(2)
В реальной интеграции стоит добавить таймаут, экспоненциальный backoff и ограничение числа попыток.
AI-экстракция вместо CSS-селекторов
Сайты часто меняют классы и структуру HTML. CSS-селекторы (div.job-title, .company > span) быстро ломаются — приходится обновлять парсер при каждом редизайне. Альтернатива — извлечение на основе LLM: вы передаёте образец HTML или текст страницы и схему полей, модель извлекает данные без привязки к конкретной вёрстке.
Преимущества подхода:
- Устойчивость к изменениям разметки.
- Возможность работать с неструктурированным текстом.
- Меньше ручной настройки для новых источников.
Недостатки:
- Выше стоимость и время обработки.
- Зависимость от качества промпта и модели.
Для стабильных площадок с предсказуемой структурой CSS-селекторы остаются быстрым и дешёвым вариантом. AI-экстракция оправдана, когда источников много или они часто меняются.
Важные нюансы
networkidle
Параметр wait_for: "networkidle" означает ожидание прекращения сетевой активности (нет запросов дольше N мс). На страницах с постоянными аналитическими запросами или long polling это может привести к долгому ожиданию или таймауту. Часто надёжнее использовать явное ожидание конкретного элемента (.job-list, .search-results).
Резидентные прокси
Площадки с вакансиями обычно защищены антибот-системами. IP из дата-центров блокируются или получают CAPTCHA. Резидентные прокси (IP домашних провайдеров) существенно повышают успешность запросов. В API для скрейпинга провайдер, как правило, сам подбирает тип прокси; при необходимости можно явно указать proxy_type: "residential".
Стоимость session_id
При использовании сессий (один браузерный контекст для нескольких запросов) расход учитывается по-другому: не только за страницу, но и за время жизни сессии. Если задача — разово собрать 100 вакансий с одной страницы, сессия может быть невыгодна. Для сценариев, где нужно сохранять cookies и state между запросами (например, вход в аккаунт), сессия, наоборот, уменьшает число повторных прохождений защиты.
Таймауты и «подвисшие» задачи
Динамические страницы иногда загружаются 10–30 секунд. Слишком короткий таймаут приведёт к частым ошибкам; слишком длинный — к лишним затратам при «подвисших» страницах. Оптимально задавать таймаут 15–30 секунд и отслеживать долю успешных запросов, подстраивая значения под конкретный сайт.
Рекомендации по внедрению
- Начните с ручной проверки — откройте сайт в браузере, воспроизведите фильтры и скролл, заметьте селекторы и порядок действий.
- Используйте
js_actionsпошагово — сначала загрузка и клик по одному фильтру, затем добавление скролла и ожидания. - Проверяйте
extract_schemaна малой выборке — убедитесь, что извлекаются все нужные поля и нет пустых значений. - Мониторьте success rate — если он падает, проверьте изменения на сайте, настройки ожидания и прокси.
- Рассмотрите кэширование — для периодического обновления не запрашивайте одни и те же страницы чаще необходимого.
Читайте также
Для стабильного парсинга площадок с вакансиями нужны резидентные прокси и корректный JavaScript-рендеринг. InfraProxy предоставляет пулы IP с высокой репутацией для работы через API скрейпинга. Оставьте заявку.
Нужны надёжные прокси для вашего проекта?
InfraProxy предоставляет серверные и резидентные прокси для российского бизнеса. Договор, постоплата, техподдержка.
Читайте также
Настройка прокси для мониторинга цен конкурентов
Пошаговое руководство по настройке прокси для мониторинга цен: ротация IP, geo-targeting, sticky-сессии, обход антибот-защит. Код на Python и bash.
РуководстваКак настроить прокси для RAG-пайплайна
Практическое руководство по настройке прокси для RAG-пайплайна: архитектура, код на Python, sticky-сессии, расписание обходов. InfraProxy, SOCKS5, 100 000+ IP.
РуководстваNo-code веб-скрейпинг: извлечение данных без программирования
Как настроить сбор данных в 2026 году без написания кода: API для скрейпинга, extract_rules, автоматизация через n8n и Make.com, батчевая обработка 100+ страниц.