Как очистить все страницы списков вакансий EngageATS? Пагинация возвращает только первую страницу (сайт Совета Бромли)Python

Программы на Python
Ответить
Anonymous
 Как очистить все страницы списков вакансий EngageATS? Пагинация возвращает только первую страницу (сайт Совета Бромли)

Сообщение Anonymous »

Я пытаюсь получить все списки вакансий с сайта по подбору персонала EngageATS, используя только стандартные библиотеки Python:

Код: Выделить всё

import requests
from bs4 import BeautifulSoup
import json
import time
Сайт, который я сканирую:

Код: Выделить всё

https://recruitmentbromley.engageats.co.uk/V2/Vacancy
На веб-сайте 54 вакансии.
На странице 1 показано 11 вакансий, а остальные 43 — на страницах 2–6 (нумерация страниц осуществляется с помощью AJAX).
Я пытаюсь воспроизвести это поведение с помощью запросов, а не Selenium.

Код: Выделить всё

import requests
from bs4 import BeautifulSoup
import json
import time

BASE = "https://recruitmentbromley.engageats.co.uk"
LIST_URL = BASE + "/V2/Vacancy"

# Suspected AJAX endpoint for pagination
PAGED_ENDPOINT_PATH = "/V2/Vacancy/_PagedSearch"

headers_get = {
"User-Agent": "Mozilla/5.0",
}

headers_post = {
"User-Agent": "Mozilla/5.0",
"X-Requested-With": "XMLHttpRequest",
}

def parse_jobs(html):
"""Extract job data from one page of HTML."""
soup = BeautifulSoup(html, "html.parser")
job_blocks = soup.select(".ats-gray-panel, .ats-white-panel")

jobs = []

for block in job_blocks:
title_el = block.select_one(".ats-heading-font")
if not title_el:
continue

title = title_el.get_text(strip=True)

# Job URL
view_btn = block.select_one("button.btn-search-results-view")
url = view_btn["data-param1"] if view_btn and view_btn.has_attr("data-param1") else None

# Location, contract type, closing date
details = block.select(".ats-normal-font")
location = contract = closing = None

for d in details:
text = d.get_text("  ", strip=True)
if text.startswith("Location:"):
location = text.replace("Location: ", "").strip()
elif text.startswith("Contract Type:"):
contract = text.replace("Contract Type: ", "").strip()
elif text.startswith("Closing Date:"):
closing = text.replace("Closing Date: ", "").strip()

# Posted date
posted_el = block.select_one(".row.p-1.text-right span")
posted = posted_el.get_text(strip=True).replace("Posted: ", "") if posted_el else None

jobs.append({
"title": title,
"url": url,
"location": location,
"contract_type": contract,
"closing_date": closing,
"posted": posted,
})

return jobs

def fetch_paged(page_no: int):
"""Fetch HTML for a given page number via suspected AJAX endpoint."""
endpoint = BASE + PAGED_ENDPOINT_PATH
print(f"\n--- Fetching page {page_no} from {endpoint} ---")

payload = {"pageNo": page_no}

r = requests.post(endpoint, data=payload, headers=headers_post)
print("Status code:", r.status_code)

if r.status_code != 200:
return None

return r.text

# --- Main scraper ---
print("Downloading first page (GET)...")
first_html = requests.get(LIST_URL, headers=headers_get, timeout=20).text
soup = BeautifulSoup(first_html, "html.parser")

# Detect total pages
last_page_btn = soup.select_one("#pagination button:last-of-type")
if last_page_btn and last_page_btn.has_attr("data-pageNo"):
try:
total_pages = int(last_page_btn["data-pageNo"])
except ValueError:
total_pages = 1
else:
total_pages = 1

print(f"Detected total_pages = {total_pages}")

all_jobs = []

# Page 1
page1_jobs = parse_jobs(first_html)
print(f"Page 1: found {len(page1_jobs)} jobs")
all_jobs.extend(page1_jobs)

# Other pages
for page in range(2, total_pages + 1):
html = fetch_paged(page)
if not html:
print(f"Skipping page {page} because fetch failed.")
continue

page_jobs = parse_jobs(html)
print(f"Page {page}: found {len(page_jobs)} jobs")
all_jobs.extend(page_jobs)

time.sleep(1)

print(f"\nTOTAL jobs collected: {len(all_jobs)}")

out_path = r"C:\Users\xxx\Downloads\bromley_jobs_full.json"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(all_jobs, f, indent=2, ensure_ascii=False)

print("Saved to:", out_path)
Проблема
Страница 1 очищается правильно (11 заданий).
Все страницы 2–6 возвращают 0 заданий.
Конечная точка /V2/Vacancy/_PagedSearch всегда возвращает 0 заданий при вызове с помощью Python.
В браузере при нажатии «Страница 2» новые результаты загружаются правильно.
Инструменты разработчика показывают, что на самом деле разбиение на страницы может использовать другую конечную точку, например:
/V2/Vacancy/ApplySearchFilter
и похоже, что он использует более сложную полезную нагрузку POST, чем просто номер страницы.
Мой вопрос
Какова правильная конечная точка и полезная нагрузка POST для разбиения на страницы EngageATS, и как я могу воспроизвести разбиение на страницы AJAX браузера с использованием библиотеки запросов, чтобы я мог получить все 54 задания?
Я хочу решить эту проблему, используя только:
запросы
bs4
json
time
Нет Selenium и никакой автоматизации браузера.
Спасибо.

Подробнее здесь: https://stackoverflow.com/questions/798 ... s-only-fir
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»