Сбор вакансий: границы браузера
Планируйте сбор публичных списков вакансий с опорой на разметку JobPosting, пагинацию, состояние сессии и границу с данными кандидатов.
BotBrowser Team
Нужна структурированная документация по теме Развертывание?
Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.
Сбор вакансий это главным образом задача пагинации и целостности записей, и браузерный уровень отвечает лишь за часть её решения. Значимая граница проходит по классу доступа: публичную страницу списка можно запросить в рамках полномочий оператора, тогда как профиль кандидата, история откликов, почта рекрутера или страница сохранённого поиска, привязанная к аккаунту, относятся к закрытым данным и выходят за пределы этой рабочей нагрузки. BotBrowser предоставляет настраиваемую среду браузера, которая запускается из конфигурации оператора и возвращает видимый результат страницы, но не может выдать разрешение, решить, допускают ли условия сайта сбор, или снять ограничение аутентификации, согласия либо частоты запросов. Этот план охватывает только публичные списки.
TL;DR
- Рассматривайте каждый сайт вакансий как три источника записей: карточки списка, страницы вакансий и JSON-LD
JobPosting. Объединяйте их в одну запись с явным порядком приоритета. - Определяйте готовность страницы как видимый контейнер результатов вместе со стабильным числом карточек, а не как фиксированную паузу или глобальное событие простоя сети.
- Передавайте
job_id,canonical_url,datePosted,validThrough,hiringOrganizationиjobLocationиз первого наблюдения, а не восстанавливайте их позже. - Классифицируйте каждый URL как публичный список или закрытые данные до первого запроса; останавливайте запуск, когда ответ переходит во второй класс.
- Относите
429, правилоrobots.txt, вставку согласия и401к разным уровням. Ни один из них не доказывает, что браузеру нужен другой профиль.
Содержание
- Сопоставить рабочую нагрузку со списком обязанностей браузера
- Провести границу между публичным и закрытым доступом
- Измерять полноту и относить сбои к уровням
Сопоставить рабочую нагрузку со списком обязанностей браузера
Запуск по сайту вакансий начинается с URL запроса, например https://jobs.example.com/jobs?q=analyst&p=1, проходит по ссылкам пагинации, которые предоставляет сайт, и открывает страницу каждой вакансии. При 40 карточках на странице и 12 страницах списка ожидаемый потолок составляет 480 страниц вакансий, но браузер никогда не определяет это число. Оператор фиксирует запрос, разрешённый диапазон страниц и правило остановки; браузер создаёт контекст, выполняет навигацию, ждёт согласованный сигнал и читает полученную страницу.
Разделение обязанностей предотвращает распространённый ложный вывод. Планировщик отвечает за повторы, порядок очереди и параллелизм. Хранилище отвечает за нормализацию и дедупликацию. Браузер отвечает за навигацию, рендеринг, готовность и извлечение того, что предоставляет страница. Когда страница вакансии возвращает карточку без заголовка, отсутствующее поле остаётся проблемой извлечения или разметки, пока код состояния или вставка не докажут обратное.
Страницы сайтов вакансий необычайно однообразны: один шаблон обслуживает тысячи объявлений, фильтры находятся в строке запроса, а разметка страницы вакансии редко меняется между ролями. Поэтому стоимость каждой страницы определяется главным образом навигацией, ожиданием готовности и разбором, а не изучением новой разметки при каждом переходе. Такая форма вознаграждает фиксированный контракт селекторов и фиксированный контракт полей и наказывает произвольные селекторы, написанные отдельно для каждой вакансии.
Та же арифметика перехода от страниц списка к страницам вакансий рассматривается в планировании нагрузки для сбора SERP, где этапы браузера отделены от ожидания ответа до выбора цели сбора.
Читать структурированную вакансию раньше видимой карточки
Многие сайты публикуют разметку Schema.org JobPosting в виде JSON-LD. Узел обычно находится в блоке <script type="application/ld+json">, иногда внутри массива @graph, а иногда распределён по нескольким блокам одной страницы. Читайте каждый блок, сохраняйте ошибки разбора как учитываемое поле и выбирайте узлы, у которых @type равен JobPosting или является массивом, содержащим это значение.
| Поле | Типичная форма | Сохранять как | Сбой, который нужно записать |
|---|---|---|---|
@type | "JobPosting" или массив | node_type | На странице есть карточки, но нет узла вакансии |
datePosted | дата ISO 8601, например 2026-11-03 | date_posted | Есть только относительный текст, например «3 days ago» |
validThrough | дата или дата-время ISO 8601 | valid_through | Поле отсутствует у бессрочных ролей |
hiringOrganization.name | строка Organization.name | employer_name | Работодатель указан как «Confidential» |
jobLocation.address.addressLocality | вложенный PostalAddress | location_city | Удалённая роль без адреса |
identifier | строка или PropertyValue | source_job_id | Идентификатор меняется между рендерингами |
Одно и то же поле может встречаться в трёх местах с разными значениями: в карточке списка, в DOM страницы вакансии и в узле JSON-LD. Записывайте, какой источник предоставил каждое поле, вместо того чтобы позволять более позднему проходу молча перезаписывать более ранний. Рабочий порядок приоритета: сначала структурированные данные, затем встроенное состояние страницы, последним идёт видимый DOM; выбранный источник сохраняется в field_source.
const raw = await page.locator('script[type="application/ld+json"]').allTextContents();
const errors = [];
const nodes = raw.flatMap(text => {
try {
const doc = JSON.parse(text);
return Array.isArray(doc['@graph']) ? doc['@graph'] : [doc];
} catch (error) {
errors.push(String(error));
return [];
}
});
const postings = nodes.filter(node => [].concat(node['@type'] ?? []).includes('JobPosting'));
Этот фрагмент возвращает узлы вакансий и оставляет ошибки разбора видимыми. Он не предполагает, что сайт публикует структурированные данные; сайту, который отрисовывает только карточки, всё ещё нужен контракт DOM, а сайту, который вставляет разметку после первой отрисовки, всё ещё нужно правило готовности, описанное ниже.
Определять пагинацию по странице, а не по угаданному URL
Сайты вакансий выражают постраничный переход как минимум пятью способами, и различие меняет то, как запуск обнаруживает конец. Записывайте URL, который создал каждую страницу, её индекс и следующую ссылку, которую объявляет сама страница.
| Шаблон | Пример | Обнаружение | Основной риск |
|---|---|---|---|
| Номер страницы | ?page=2 | параметр запроса | Сайт ограничивает или игнорирует глубокие страницы |
| Короткий номер страницы | ?p=2 | параметр запроса | Одно и то же имя параметра означает разное на другом сайте |
| Смещение | ?start=25 | параметр запроса с размером страницы | Смещение рассчитано по неверному размеру страницы |
| Курсор | ?after=eyJpZCI6MTg0Mn0 | непрозрачный токен с предыдущей страницы | Повторное использование устаревшего курсора |
| Отношение ссылки | <link rel="next" href="..."> | атрибут DOM | Отсутствует в некоторых шаблонах |
Останавливайтесь, когда страница не объявляет следующую ссылку, когда контейнер результатов присутствует, но пуст, когда ключи первой и последней карточки повторяют более раннюю страницу или когда набор посещённых URL уже содержит следующую цель. Считайте число страниц из виджета «1 of 12» подсказкой, а не гарантией: изменения фильтров, персонализация и истёкшие вакансии могут сдвигать итог между запросами.
Две защиты должны присутствовать в цикле с первого запуска. Храните набор нормализованных URL страниц и отклоняйте повтор. Храните подпись, построенную из ключей первой и последней карточки на каждой странице; одинаковые подписи на соседних страницах обычно означают, что сайт ограничил номер страницы вместо возврата новых результатов.
Задавать условие готовности, соответствующее извлечению
Событие навигации доказывает, что документ получен, а не что список пригоден для использования. DOMContentLoaded может сработать до отрисовки карточек, а глобальное ожидание простоя сети может застрять на рекламе, аналитике или длинном опросе. Используйте условие, привязанное к элементам, которые читает экстрактор.
| Сигнал готовности | Что он доказывает | Что он упускает |
|---|---|---|
событие domcontentloaded | HTML разобран | Карточки, отрисованные на клиенте |
| контейнер списка видим | Шаблон смонтирован | Карточки всё ещё поступают |
| число карточек стабильно при двух проверках | Список установился | Поздняя вставка JSON-LD |
узел JobPosting присутствует | Структурированные данные страницы вакансии разобраны | Сайты без разметки |
| следующий элемент управления доступен | Пагинация интерактивна | Ограничение страницы на сервере |
Практическое правило представляет собой конъюнкцию: контейнер результатов видим, число карточек равно числу, которое сообщает страница, и каждая карточка имеет непустую ссылку на вакансию. Для страницы вакансии добавьте разобранный узел JobPosting либо явный маркер «нет структурированных данных» до начала извлечения. Записывайте отметку времени события готовности отдельно от начала запроса, чтобы последующий разбор мог вычислить ожидание.
Состояние сессии относится к тому же контракту. Некоторые сайты хранят фильтры в URL и возвращают одинаковый список новому контексту. Другие хранят порядок сортировки, радиус поиска или курсор страницы в cookie либо локальном хранилище, поэтому новый контекст начинает с первой страницы, хотя URL всё ещё указывает на восьмую. Определите, какое поведение применимо, затем либо используйте один контекст для запуска, либо восстанавливайте состояние фильтров из URL. Отдельный контекст на запуск также не даёт хранилищу перетекать между несвязанными заданиями сбора; изоляция браузера для нескольких аккаунтов описывает, как хранилище и настройки идентичности разделяются по контекстам, когда несколько разрешённых рабочих нагрузок используют один хост.
Если запуск теряет маршрут посреди пагинации, не перезапускайте весь обход. Руководство восстановление сессии после сетевого прерывания описывает, как отличить разорванное соединение от истёкшей страницы и как продолжить без дублирования завершённых страниц.
Провести границу между публичным и закрытым доступом
Класс доступа URL это свойство сайта и полномочий оператора, а не сборки браузера. Публичный результат поиска, публичная страница вакансии и карьерная страница компании, которая отдаётся без сессии, принадлежат одному классу. Профиль кандидата, страница статуса отклика, цепочка сообщений рекрутера, сохранённый поиск, привязанный к аккаунту, и форма отклика, принимающая резюме, принадлежат другому. Эта рабочая нагрузка запрашивает только первый класс.
Эта граница публичных данных совпадает с той, которую удерживает защита отпечатков для сбора веб-данных: сигналы браузера можно сделать воспроизводимыми для разрешённого процесса, но они не превращают закрытую страницу в публичные данные.
| URL или класс ответа | Пример пути | Ожидаемый ответ | В области | Действие |
|---|---|---|---|---|
| Публичный поиск | /jobs?q=analyst&p=2 | 200 с карточками результатов | Да | Запрашивать в согласованном диапазоне страниц |
| Публичная вакансия | /jobs/1842 | 200 с разметкой вакансии | Да | Извлечь и записать |
| Публичная страница компании | /careers/acme | 200 без сессии | Да, если разрешено | Рассматривать как ещё один источник списка |
| Форма входа | /login | 200 с формой входа | Нет | Исключить путь |
| Профиль кандидата | /account/profile | 401 или 403, либо 200 только с сессией | Нет | Остановиться и исключить |
| История откликов | /account/applications | 200 в рамках сессии | Нет | Остановиться и исключить |
| Почта рекрутера | /employer/messages | 200 в рамках сессии | Нет | Остановиться и исключить |
| Загрузка резюме | /apply/1842/upload | форма, принимающая личные файлы | Нет | Остановиться и исключить |
Применяйте таблицу до первой навигации. Список разрешённых URL проще проверять, чем список запрещённых, поскольку новый маршрут аккаунта, добавленный сайтом, не сможет молча попасть в запуск. Каждая запись должна содержать ссылку на согласование оператора, дату проверки и назначенный класс страницы.
Правила распознавания границы аутентификации
Самый явный сигнал это HTTP 401 с заголовком WWW-Authenticate, но реальные сайты также перенаправляют на /login, возвращают 403 для защищённого маршрута или отдают страницу 200, единственное содержимое которой составляет приглашение войти. Пункт навигации, доступный только кандидату, аватар аккаунта или элемент «apply», открывающий форму резюме, служат маркером границы даже при статусе 200.
Когда появляется любой из них, остановите обработку этого URL, запишите класс как authenticated и не включайте его в знаменатель полноты публичного списка. Никогда не передавайте учётные данные, не воспроизводите cookie, скопированную из сессии человека, не проходите проверку и не продолжайте через страницу, запрашивающую данные кандидата. Правильный результат это исключение с причиной, а не частично заполненная запись.
Согласие, robots и ограничения частоты как отдельные ворота
Вставка согласия это политические ворота. Баннер может скрывать список до выбора, и доступные варианты могут различаться по региону и по тому, существует ли уже сессия. Запишите consent_state как present, required или absent, сохраните URL и передайте решение оператору. Скриптовый клик принятия не входит в план извлечения публичного списка, поскольку выбор принадлежит посетителю, а не сборщику.
Robots Exclusion Protocol определяет, как сайт публикует правила обхода, включая группы, выбранные по токену user-agent. Получите robots.txt, запишите статус ответа и правило, применимое к объявленному клиенту, и исключите запрещённый путь. Выбор другого токена user-agent, чтобы попасть в более разрешающую группу, является обходом политики, а не требованием браузера.
Ответ 429 с Retry-After это ограничение частоты, определённое вместе с другими семантиками статусов в RFC 9110. Соблюдайте окно, сократите запрашиваемую область или запросите у владельца сайта API. Ротация маршрутов или профилей ради продолжения запросов в течение окна меняет получателя запроса, а не допустимость самого запроса. 503 обозначает состояние сервиса; рассматривайте его как сбой доступности и повторяйте только по согласованной политике оператора.
Матрица отнесения сбоев
Приведённая ниже матрица не позволяет ошибочно принять заблокированный ответ за дефект браузера. Каждая строка называет уровень, создавший наблюдение, и следующее разрешённое действие.
| Наблюдение | Ответственный уровень | Что оно не доказывает | Следующее действие |
|---|---|---|---|
200, но в контейнере нет карточек | Рендеринг, контракт селекторов или пустой результат | Что сайт заблокировал браузер | Проверить готовность, селектор и состояние запроса; записать empty_result |
200 с диалогом согласия | Политика согласия | Что страница недоступна | Записать consent_gate и остановиться для решения оператора |
401 вместе с WWW-Authenticate | Аутентификация | Что другой профиль был бы авторизован | Исключить URL |
403 на маршруте кандидата | Авторизация | Что маршрут публичен, но настроен неверно | Исключить URL |
Перенаправление на /login | Аутентификация | Проблема сети или локали | Исключить URL |
429 вместе с Retry-After | Ограничение частоты | Что идентичность браузера неверна | Соблюдать окно или сократить область |
Путь указан в robots.txt | Политика обхода сайта | Что HTML повреждён | Исключить путь |
| Страница проверки вместо результатов | Контроль доступа | Что параметр рендеринга устранит её | Остановиться; не автоматизировать проверку |
| Тайм-аут навигации | Сеть, хост или вес страницы | Что сайт отклонил запрос | Записать этап и перепроверить маршрут |
200 с результатами меньше одной страницы | Конец пагинации или сужение фильтра | Что запуск завершил запрос | Сравнить подпись первой страницы и запрос |
Профиль браузера, маршрут прокси или другая локаль меняют то, что наблюдает сервер. Ни одно из этих изменений не создаёт разрешение, не удовлетворяет правилу robots, не принимает выбор согласия от имени посетителя и не превращает 403 в авторизацию. Сбор допускается владельцем сайта, применимыми условиями и законом, а браузерный уровень может лишь оставаться внутри этого решения или за его пределами.
Сетевая маршрутизация представляет отдельную поверхность конфигурации. Руководство настройка прокси браузера описывает выбор маршрута, обработку учётных данных и поведение DNS; оно также поясняет, что маршрут не подтверждает политику назначения. Используйте маршрут, на который у оператора есть полномочия, и сохраняйте маршрут неизменным при сравнении поведения извлечения.
Измерять полноту и относить сбои к уровням
Полезный вопрос измерения для этой рабочей нагрузки таков: на разрешённом наборе публичных URL списков сколько вакансий обнаружено, сколько обязательных полей заполнено и какие сбои пришли с какого уровня. У этого вопроса есть защитимый знаменатель. У «доли успеха», измеренной по странице входа или ответу с ограничением частоты, его нет.
Зафиксируйте входные данные до первого запуска. Набор должен перечислять URL запросов, ожидаемый диапазон страниц, контракт готовности и контракт селекторов. Храните рядом ссылку на согласование. Если сайт меняет шаблон, набор меняется как новая версия, а не редактируется на месте, чтобы старые и новые результаты оставались сопоставимыми.
Схема записи
Храните одну запись на каждую наблюдённую вакансию и одну запись на каждую страницу. Запись страницы содержит статус, хронометраж и число. Запись вакансии содержит идентичность и источники полей, как в этом примере с иллюстративными значениями.
{
"job_id": "1842",
"canonical_url": "https://jobs.example.com/jobs/1842",
"page_url": "https://jobs.example.com/jobs?q=analyst&p=2",
"http_status": 200,
"observed_at": "2026-11-07T14:30:00Z",
"date_posted": "2026-11-03",
"valid_through": "2026-12-01",
"employer_name": "Example Analytics",
"location_city": "Toronto",
"field_source": "jsonld",
"etag": "W/\"abc123\"",
"content_hash": "9f2c1d7a4b8e0c31"
}
Сохраняйте job_id как собственный идентификатор сайта, когда он стабилен. Сохраняйте canonical_url как адрес, объявленный страницей, а не как URL с пагинацией, который привёл к ней. Сохраняйте page_url, чтобы дубликат можно было проследить до страницы списка, которая его создала. Храните etag и content_hash, чтобы обнаруживать неизменённые страницы вакансий и избегать перезаписи идентичных записей.
Объединять дубликаты по объявленному ключу
Одна и та же вакансия часто появляется на странице поиска сайта, странице компании, региональном зеркале и агрегаторе. Идентификаторы вакансий служат самым сильным ключом, когда они существуют, но повторная публикация может получить новый идентификатор, сохранив того же работодателя, заголовок, город и описание.
| Ключ-кандидат | Стабильность | Риск коллизии | Когда использовать |
|---|---|---|---|
source_job_id плюс хост сайта | Высокая, пока вакансия существует | Один и тот же id переиспользуется после удаления | Сайт публикует стабильный id |
canonical_url | Высокая | Два URL для одной заявки | Каноническая ссылка присутствует |
Работодатель, заголовок, город, date_posted | Средняя | Одна роль открыта дважды в один день | Id и канонический URL отсутствуют |
| Работодатель, заголовок, город, хеш описания | Средняя | Одинаковый текст шаблона в разных командах | Описание содержательно |
| Только заголовок и город | Низкая | Многие настоящие вакансии имеют одинаковый заголовок | Никогда, кроме подсказки для проверки |
Объединяйте по самому сильному доступному ключу, затем сохраняйте более слабые совпадения как variant_urls, а не удаляйте их. Объединённая запись должна сохранять каждый URL, который в неё внёс вклад, время первого и последнего наблюдения и источник, предоставивший каждое поле.
Матрица решения о дубликатах
| Ситуация | Объединять | Причина |
|---|---|---|
Одинаковый canonical_url, разный page_url | Да | Одна вакансия достигнута через две страницы списка |
Одинаковые работодатель, заголовок, город и date_posted; разный job_id | Да, с вариантами | Вероятно, одна заявка синдицирована дважды |
| Одинаковые работодатель и заголовок, разный город | Нет | Местоположение является частью роли |
Одинаковые работодатель, заголовок и город, date_posted различается более чем на 30 дней | Нет, если не совпадает хеш описания | Более поздняя вакансия может быть настоящей новой ролью |
valid_through прошёл, но страница всё ещё возвращает 200 | Сохранить, пометить expired | Страница не была удалена по расписанию |
| Копия агрегатора и карьерная страница компании | Объединить в запись компании, когда обе разрешены | Исходная страница служит лучшей канонической целью |
| Цепочка перенаправлений заканчивается новым каноническим URL | Объединить и сохранить цепочку | Старый URL больше не идентифицирует вакансию |
План запуска и отметки хронометража
Приведённый ниже план использует объект контракта сайта, чтобы селекторы и правило готовности были названы в одном месте. Имена иллюстративны; замените их контрактом, согласованным для разрешённого сайта.
const contract = {
list: "[data-testid='job-card']",
detailLink: "a[data-testid='job-link']",
next: "a[rel='next']",
};
const started = performance.now();
const response = await page.goto(listUrl, { waitUntil: 'domcontentloaded' });
await page.locator(contract.list).first().waitFor({ state: 'visible' });
const cards = page.locator(contract.list);
const count = await cards.count();
await page.waitForTimeout(250);
const stabled = await cards.count();
if (count !== stabled) throw new Error('card count changed before extraction');
const links = await cards.locator(contract.detailLink).evaluateAll(nodes => nodes.map(node => node.href));
Записывайте request_start, response_received, ready_signal и extract_done как отдельные отметки времени. Вычисляйте ожидание готовности как ready_signal - request_start, а время извлечения как extract_done - ready_signal. Если запуск сообщает одну длительность для страницы, медленный селектор и медленный сервер становятся неразличимы. Документация по оптимизации производительности описывает настройку среды выполнения для существующего развёртывания; отметки выше показывают, с чем следует сравнивать эту настройку.
Используйте три повторения одного и того же набора и сообщайте минимум, медиану и максимум для каждого этапа вместо лучшего запуска. Оставляйте сбойные страницы в знаменателе. Страница, у которой истёк тайм-аут до готовности, считается попыткой страницы с этапным сбоем, а не отсутствующей вакансией.
Полнота и покрытие полей
Полнота сравнивает обнаруженные вакансии с вакансиями, которые сообщает разрешённый запрос. Если первая страница сообщает 480 результатов при 40 на страницу, а запуск извлекает 462 уникальные вакансии после объединения, сообщите 462 / 480, а также число дубликатов и число исключений. Не повышайте числитель молча, объединяя несвязанные роли, и не понижайте знаменатель, отбрасывая сбойные страницы.
Покрытие полей образует отдельное отношение для каждого поля. Сообщите date_posted, присутствующий в 440 из 462 записей, valid_through в 301 и jobLocation в 455. У сайта может быть полная пагинация и слабое покрытие полей или наоборот. Два числа отвечают на разные вопросы, а единая «доля успеха» скрывает, какое из них нарушено.
Матрица отнесения сбоев по записям
| Картина в записи | Интерпретация для проверки | Следующее измерение |
|---|---|---|
| Карточки есть, JSON-LD отсутствует на каждой странице вакансии | Сайт может не публиковать разметку | Подтвердить на второй версии шаблона перед изменением извлечения |
Ожидание готовности растёт, статус остаётся 200 | Изменился клиентский рендеринг или вес скриптов | Сравнить отметки готовности на той же версии набора |
Число страниц 200 сокращается без изменения запроса | Ограничение пагинации или состояние фильтра | Перепроверить первую страницу и цепочку следующих ссылок |
429 появляется только при более высоком параллелизме | Частота запросов превысила лимит сайта | Снизить параллелизм или запросить API |
403 появляется только на путях аккаунта | Достигнута граница аутентификации | Исключить путь и проверить список разрешённых URL |
| Доля дубликатов растёт после изменения шаблона | Новый шаблон URL или идентификатора | Сравнить ключи объединения между версиями |
| Покрытие полей падает, а число карточек сохраняется | Дрейф разметки или селектора | Сравнить field_source по полям |
Контрольный список перед запуском
- Каждый исходный URL является публичным списком или публичной страницей вакансии со ссылкой на согласование.
- Маршруты аккаунта, отклика, резюме, сообщений и сохранённого поиска исключены списком разрешённых URL.
-
robots.txtполучен, применимое правило записано. - Условие готовности записано как видимый элемент плюс стабильное число.
- Правило остановки пагинации включает посещённые URL и подпись повторяющейся страницы.
- Ключ объединения и более слабые резервные ключи объявлены до извлечения.
- Статус, хронометраж и число страницы хранятся рядом с записями вакансий.
- Сбойные и исключённые страницы остаются в знаменателе с кодом причины.
- Запуск останавливается на
401,403, вставке согласия, странице проверки или429. - Запись запуска называет сборку браузера, использованный маршрут и версию набора.
FAQ
Достаточно ли JSON-LD JobPosting для идентификации каждой вакансии?
Нет. Покрытие различается по сайтам и шаблонам, а некоторые страницы отрисовывают карточки из внутреннего объекта состояния вовсе без блока структурированных данных. Используйте JSON-LD как предпочтительный источник, сохраняйте контракт DOM как резерв и записывайте field_source, чтобы падение покрытия было видно. Страница с карточками и без узла вакансии даёт наблюдение разметки, а не сбой доступа.
Следует ли останавливать пагинацию на первой пустой странице?
Остановитесь и затем проверьте. Пустой контейнер может означать, что запрос исчерпан, фильтр сузил набор или номер страницы был ограничен. Сравните подпись страницы, объявленный итог и следующую ссылку, прежде чем записывать запуск как завершённый. Одна пустая страница служит условием остановки запросов, а не доказательством, что запрос ничего не вернул.
Определяет ли профиль браузера, разрешён ли сбор?
Нет. Разрешение исходит от владельца сайта, применимых условий и закона. Профиль меняет наблюдаемые сигналы браузера, а прокси меняет маршрут, но ни то, ни другое не удовлетворяет правилу robots, выбору согласия или ограничению частоты. Таблица классов доступа и матрица сбоев образуют поверхность решения; конфигурация браузера находится ниже этого решения.
Практический вывод
Начните с классификации каждого URL как публичного списка или закрытых данных и удалите второй класс до любого запроса. Затем определите сигнал готовности, правило остановки пагинации и ключ объединения как один письменный контракт. Измеряйте полноту и покрытие полей отдельно и относите каждый сбой к уровню статуса, политики или рендеринга, который его создал.
Следующее действие невелико: составьте список разрешённых URL и ссылку на согласование для одного разрешённого публичного запроса на сайте вакансий, затем подтвердите, что первая страница возвращает 200 с узлом JobPosting или согласованным контрактом DOM. Всё остальное в запуске зависит от сохранения этой границы.
Источники
Эта статья входит в руководство: Развёртывание и выпуск браузера: операционная карта
Похожие статьи
Масштабный сбор SERP: требования к браузеру
РазвертываниеПрофили браузера для SEO-мониторинга и отслеживания SERP
РазвертываниеЗащита отпечатков браузера при сборе веб-данных
РазвертываниеНадёжные скриншоты в headless-браузере
РазвертываниеМасштабирование изолированных контекстов BotBrowser
СетьМониторинг цен в ecommerce: браузерный слой
Выполните ту же проверку в BotBrowser
Загрузите профиль, сохраните внутри браузера те сигналы, о которых только что прочитали, и подтвердите результат на своей машине до раскатки.