Код: Выделить всё
import requests
from bs4 import BeautifulSoup
import json
import time
Код: Выделить всё
https://recruitmentbromley.engageats.co.uk/V2/Vacancy
На странице 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
Мобильная версия