Все статьиРуководства6 мин

Как парсить вакансии с динамическими фильтрами

Извлечение данных с площадок с вакансиями: работа с динамическими фильтрами через API для скрейпинга, рендеринг JavaScript и AI-экстракция.

Команда InfraProxy

21 февраля 2026 г.

#вакансии#динамические фильтры#API#Playwright#извлечение данных

Почему динамические фильтры ломают традиционный парсинг

Современные площадки с вакансиями загружают контент через JavaScript. Пользователь выбирает регион, зарплатную вилку или сферу деятельности — и страница обновляется без полной перезагрузки. Вакансии подтягиваются асинхронно через API или перерисовываются на клиенте. Для классического скрейпинга это создаёт несколько проблем:

  1. Пустой HTML при первом запросе — сервер отдаёт лишь оболочку страницы, список вакансий рендерится в браузере после выполнения скриптов.
  2. Фильтры требуют взаимодействия — нужно кликнуть по элементам, заполнить поля, подождать обновления списка.
  3. Бесконечная прокрутка и «Загрузить ещё» — контент подгружается по мере скролла или по нажатию кнопки.
  4. Меняющаяся структура 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 секунд и отслеживать долю успешных запросов, подстраивая значения под конкретный сайт.

Рекомендации по внедрению

  1. Начните с ручной проверки — откройте сайт в браузере, воспроизведите фильтры и скролл, заметьте селекторы и порядок действий.
  2. Используйте js_actions пошагово — сначала загрузка и клик по одному фильтру, затем добавление скролла и ожидания.
  3. Проверяйте extract_schema на малой выборке — убедитесь, что извлекаются все нужные поля и нет пустых значений.
  4. Мониторьте success rate — если он падает, проверьте изменения на сайте, настройки ожидания и прокси.
  5. Рассмотрите кэширование — для периодического обновления не запрашивайте одни и те же страницы чаще необходимого.

Читайте также

Для стабильного парсинга площадок с вакансиями нужны резидентные прокси и корректный JavaScript-рендеринг. InfraProxy предоставляет пулы IP с высокой репутацией для работы через API скрейпинга. Оставьте заявку.

Нужны надёжные прокси для вашего проекта?

InfraProxy предоставляет серверные и резидентные прокси для российского бизнеса. Договор, постоплата, техподдержка.