Слогер Создать блог
Разработка

Почему фильтры API молча не работают и как это проверить

Публичный поиск вакансий LinkedIn принимает параметры по типу занятости, опыту и удалёнке — и отдаёт один и тот же результат. Разбираю, как ловить такие тихие сбои и почему фильтрация после выгрузки требует отдельного бюджета сканирования.

Запрос вернулся с кодом 200, JSON на месте, строк ровно столько, сколько просили. Данные при этом — мусор. Так выглядит самый неприятный сбой в парсинге: сервер не ругается, не отдаёт ошибку, просто игнорирует часть параметров и возвращает дефолтную выдачу.

Как это выглядит на живом примере

Публичный поиск вакансий LinkedIn без логина идёт через guest-эндпоинт /jobs-guest/jobs/api/seeMoreJobPostings/search. Ему можно передавать те же параметры, что и на сайте: f_JT — тип занятости, f_E — уровень опыта, f_WT — удалёнка, sortBy=DD — сначала новые, f_TPR — за какой период, f_AL и f_EA — Easy Apply.

Примерно с 11 сентября 2026 эндпоинт принимает все эти параметры и возвращает один и тот же набор независимо от того, что вы отправили. Проверка: keywords=data analyst, location=United States, пять вариантов запроса, первые 50 ID сравниваются с выдачей без фильтров (повтор 13 сентября 2026).

Вариант запросаПересечение с выдачей без фильтров
f_JT=C (контракт)50 / 50
f_E=2 (начальный уровень)50 / 50
f_WT=2 (удалёнка)50 / 50
sortBy=DD (сначала новые)50 / 50
f_TPR=r86400 (за 24 часа)4 / 50 — работает

Только f_TPR, f_AL и f_EA меняют ответ. Остальное сервер молча проглатывает. Ни ошибки, ни предупреждения о том, что фильтр проигнорирован, — просто дефолтная первая страница.

Почему это опаснее явной поломки

Пятисотая ошибка или таймаут видны сразу: скрипт падает, вы идёте чинить. Здесь всё наоборот. Парсер, который доверяет параметрам, «успешно» работает с неправильными данными: логи чистые, метрики зелёные, а в базу льются удалённые вакансии в ответ на запрос «только контракт». Находится такое через месяцы, когда кто-то вручную сверяет выборку и удивляется, откуда в отчёте взялись лишние строки.

Правило, которое стоит повесить на стену: параметр, который принимается, — не то же самое, что параметр, который применяется. Проверять надо эффект, а не код ответа.

Фильтрация после выгрузки: где она ломается

Первый очевидный вариант — тянуть вакансии как есть, потом открывать карточку каждой (в детальной странице критерии ещё живут: уровень, тип занятости) и выбрасывать несовпадающие. Я так и сделал. Работало ровно до следующего аудита параметров, который показал баг: при maxJobs: 20 и jobTypes: ["contract"] прогон вернул 0 строк.

Не потому что контрактных вакансий нет. Просто до фильтра успело просканироваться 20 карточек, и ни одна из них не оказалась контрактом. В моей выборке первая такая вакансия нашлась на 49-й детальной странице.

Вывод: если фильтр применяется на клиенте, бюджет сканирования нужно отвязывать от бюджета результата. Иначе редкие фильтры всегда возвращают пустоту, и вы будете уверены, что таких вакансий не существует.

Как это устроено теперь: страница → проверка деталей → оставить совпадения → листать дальше, пока не наберётся maxJobs совпадений или не упрётся в лимит сканирования (по умолчанию 5× maxJobs, минимум 50, максимум 500). Несовпавшие вакансии не съедают бюджет результата. Прогон «удалённый data analyst, 8 штук» просматривает около 30 объявлений; «контракт» просматривает заметно больше — и честно об этом сообщает.

Когда клиентская фильтрация оправдана

Если условие пропускает большинство вакансий, цена почти не растёт: на каждую просмотренную карточку приходится совпадение. Когда под условие попадает одна вакансия из пятидесяти, число запросов к детальным страницам взлетает вместе с временем прогона. В такой ситуации дешевле сузить входной запрос по ключевым словам или географии, а тонкую фильтрацию уже делать на клиенте.

Регрессионная проверка, которая ловит это сама

Разовая проверка бесполезна: параметр могут отключить в любой момент, без анонса и записи в changelog. Нужен ежедневный прогон-«ворота» с заранее описанным ожидаемым эффектом. У меня это JSON-спека: вариант входа → что должно получиться.

  • фильтр по контракту → все строки с employmentType, содержащим contract;
  • фильтр по удалёнке → минимум 8 строк в выдаче;
  • сортировка по дате → первые 5 ID отличаются от базовой выдачи.

Эту спеку я запускаю каждый день. Баг с нулём строк она поймала на следующий день после того, как появилась.

Чек-лист для любого скрапера с фильтрами

  1. Соберите эталон: выдача без фильтров, первые 50 ID. Каждый фильтр сравнивайте с ней по пересечению, а не по количеству строк. Совпало 50 из 50 — параметр, скорее всего, мёртвый.
  2. Проверяйте не только фильтры, но и сортировку. sortBy притворяется работающим так же убедительно.
  3. Разделите бюджеты: сколько страниц сканируем и сколько результатов хотим получить. Поставьте потолок на сканирование и логируйте, когда в него упираетесь.
  4. Пишите ожидаемый эффект в машинночитаемом виде. «Все строки подходят под условие» проверяется автоматически, «выглядит нормально» — нет.
  5. Показывайте цену фильтра. Если на 8 удалённых вакансий ушло 300 просканированных карточек, это должно быть видно вам через месяц, а не только в момент запуска.

Молчаливый сбой параметров — норма для публичных API без документации и без гарантий, а не редкая аномалия. Защита здесь одна: измерять эффект каждого параметра и делать это регулярно. Одна таблица с ID и один ежедневный прогон обходятся дешевле, чем квартальный отчёт, собранный на неверных данных.

По материалам: career. Текст переработан редакцией Слогера.

← На главную

Рекламное место — Конец поста
Реклама · Слогер

Комментарии (0)

Войдите, чтобы комментировать.

Пока нет комментариев. Будьте первым.