HTTP под капотом: запросы, ответы и коды статуса
Скрейпер общается с сайтом на языке HTTP — том же, на котором говорит браузер.
HTTP (HyperText Transfer Protocol) — протокол «запрос-ответ»: клиент отправляет запрос с методом, адресом и заголовками, сервер возвращает ответ с кодом статуса, заголовками и телом.
Чтобы скрейпить осознанно, нужно понимать, что именно уходит на сервер и что приходит обратно. Когда ты открываешь страницу в браузере, под капотом происходит ровно то же, что будет делать твой скрипт: отправляется HTTP-запрос и приходит HTTP-ответ. Разница только в том, что браузер ещё и красиво рисует результат.
Анатомия запроса и ответа
Запрос состоит из метода (GET — «дай данные», POST — «прими данные»), URL (адрес ресурса), заголовков (метаданные: какой браузер, какой язык, какие куки) и иногда тела (для POST). Ответ состоит из кода статуса, заголовков и тела — обычно это и есть HTML-страница.
КЛИЕНТ (скрейпер) СЕРВЕР
│ │
│ GET /catalog HTTP/1.1 │
│ Host: shop.example │
│ User-Agent: my-bot/1.0 ----------> │
│ │ ищет страницу
│ HTTP/1.1 200 OK │
│ Content-Type: text/html │
│ <html>...</html> <---------- │
│ │Коды статуса, которые встретит скрейпер
| Код | Значение | Что делать скрейперу |
|---|---|---|
| 200 | OK — всё хорошо | Парсить тело ответа |
| 301/302 | Редирект | Перейти по новому адресу (requests делает это сам) |
| 403 | Доступ запрещён | Возможно, сайт распознал бота — проверь заголовки |
| 404 | Не найдено | Страницы нет — пропустить |
| 429 | Too Many Requests | Ты шлёшь слишком часто — притормози! |
| 500/503 | Ошибка/перегрузка сервера | Подождать и повторить позже |
Как работает под капотом
URL сам по себе несёт структуру, которую полезно уметь разбирать. В стандартной библиотеке Python есть модуль urllib.parse — он работает прямо в браузере (Pyodide) и не требует интернета. Разберём адрес на части:
Попробуй сам ▶
from urllib.parse import urlparse, parse_qs
url = 'https://shop.example/catalog?page=2&sort=price&q=ноутбук'
p = urlparse(url)
print('схема :', p.scheme)
print('хост :', p.netloc)
print('путь :', p.path)
print('запрос:', p.query)
# query-string -> словарь
params = parse_qs(p.query)
for key, val in params.items():
print(f' {key} = {val[0]}')Так скрейпер понимает, как менять параметры пагинации (page=2, page=3) или поисковый запрос, не открывая сайт руками. Это основа обхода каталогов по страницам.
Заголовки запроса: что именно ты сообщаешь серверу
Каждый запрос несёт заголовки — пары «имя: значение», описывающие отправителя. User-Agent сообщает, какой клиент пришёл; Accept — какие форматы ответа подходят; Accept-Language — предпочитаемый язык; Referer — с какой страницы был переход; Cookie — сохранённое состояние сессии. Браузер заполняет всё это автоматически, а вот requests по умолчанию отправляет минимум — и именно поэтому «голый» запрос часто получает 403: серверу он выглядит подозрительно безликим.
Ответ тоже несёт заголовки, и их полезно читать. Content-Type подсказывает, HTML перед тобой или JSON. Content-Encoding говорит о сжатии (gzip). Set-Cookie выдаёт куки для следующих запросов. Retry-After в ответе 429 прямо указывает, через сколько секунд можно повторить — игнорировать эту подсказку и значит вести себя невежливо. Понимание заголовков превращает скрейпер из «слепого» клиента в осознанного участника HTTP-диалога.
Частые ошибки
- Не смотреть на код ответа. Получив 403 или 429, наивный скрейпер парсит страницу-заглушку и сохраняет мусор.
- Игнорировать 429. Это прямой сигнал «ты слишком частишь» — продолжать долбить нельзя.
- Думать, что заголовки не важны. Часто именно отсутствие нормального
User-Agentприводит к 403.
Best practices
- Всегда проверяй
response.status_codeперед парсингом. - На 429 и 503 делай паузу и повторяй с увеличивающейся задержкой (экспоненциальный бэк-офф).
- Отправляй честный, описательный
User-Agent— об этом в разделе про этику.
Стоит запомнить и про идемпотентность методов. GET по смыслу безопасен и не должен менять состояние сервера — именно его использует скрейпер, когда просто читает данные. POST, PUT, DELETE меняют данные, и применять их при сборе информации почти никогда не нужно. Это ещё один этический ориентир: читающий скрейпер ограничивается GET и не трогает то, что способно изменить чужую систему. Чёткое понимание семантики методов удерживает тебя в безопасной зоне «только чтение публичного».
Итог: HTTP — это диалог «запрос-ответ». Скрейпер обязан читать код статуса и реагировать на 403/429/503. Разбор URL через urllib.parse — первый практический навык сбора данных.