Зачем DE разбираться в HTTP
90% внешних источников данных в работе data-инженера — это
requests.
Поэтому первый урок этого модуля — не про код. Это про то, как устроен HTTP на уровне, который нужен, чтобы читать логи, понимать, что говорит сервер, и не наступить на классические грабли вроде «а почему оно повторило POST три раза и я списал деньги клиенту трижды».
К концу урока вы должны без подсказок отвечать на вопросы: что значит 429, какие методы можно повторять, что положить в Authorization, и почему verify=False в production — это увольнение.
HTTP запрос-ответ за 30 секунд
Минимальный запрос выглядит так:
GET /repos/python/cpython HTTP/1.1
Host: api.github.com
Accept: application/vnd.github+json
User-Agent: my-etl/1.0
Минимальный ответ:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 4321
X-RateLimit-Remaining: 4998
{"id": 1456, "name": "cpython", "stargazers_count": 60000}
Из этих двух блоков складывается вся работа с REST API. Всё остальное — это варианты заголовков, методов, статусов и тел.
Каждое сообщение состоит из стартовой строки, заголовков и опционального тела, разделённых пустой строкой
HTTP/1.1 vs HTTP/2 vs HTTP/3
Краткая awareness, не более.
HTTP/3 поверх
requests работает только по HTTP/1.1. httpx умеет HTTP/2 опционально (если установить с [http2]). Для подавляющего большинства задач разница незаметна.
Методы
Запрос всегда начинается с метода — глагола, описывающего, что мы хотим сделать с ресурсом.
| Метод | Что значит | Тело? | Идемпотентен? |
|---|---|---|---|
GET | Прочитать ресурс | Нет | Да |
POST | Создать новый ресурс / выполнить действие | Да | Нет |
PUT | Заменить ресурс целиком | Да | Да |
PATCH | Частично обновить ресурс | Да | По договорённости |
DELETE | Удалить ресурс | Обычно нет | Да |
HEAD | Как GET, но без тела ответа | Нет | Да |
OPTIONS | Узнать, что разрешено на этом URL | Нет | Да |
GET, PUT, DELETE — идемпотентные по определению протокола. Сервер обязан их так реализовывать. POST — нет. Если для конкретного POST вы хотите идемпотентность, нужен
POST /v1/charges HTTP/1.1
Host: api.stripe.com
Idempotency-Key: 7b3e9f1a-8c4e-4b9a-9f2e-1a2b3c4d5e6f
Запомните: ретрай-логика, которую мы напишем в уроке 04, должна знать, идемпотентен ли запрос. Иначе она вам однажды задвоит данные в проде.
Status codes
Ответ всегда начинается со статус-кода — трёхзначного числа. Первая цифра определяет класс ответа.
2xx — успех.
200 OK— стандартный успех, тело ответа есть.201 Created— создан новый ресурс. В заголовкеLocationобычно URL созданного.202 Accepted— запрос принят, обработка асинхронна. Будет позже.204 No Content— успех, тело пустое. Часто послеDELETEилиPUT.
3xx — редирект.
301 Moved Permanently— ресурс переехал навсегда, вLocationновый URL.302 Found— переехал временно.304 Not Modified— у клиента уже свежая версия (поIf-Modified-Since/ETag). Полезно для кэширования.
requests и httpx редиректы обрабатывают сами по умолчанию. Можно отключить через allow_redirects=False.
4xx — клиент ошибся, повторять не нужно.
400 Bad Request— мусор в запросе, мы что-то невалидное послали.401 Unauthorized— нет токена / токен невалидный.403 Forbidden— токен валидный, но прав нет.404 Not Found— ресурса по этому URL не существует.409 Conflict— состояние ресурса не позволяет операцию.422 Unprocessable Entity— JSON распарсился, но валидация по бизнес-правилам провалилась.429 Too Many Requests— превысили. Это единственная 4xx, которую обычно стоит повторить (с задержкой).rate limit
5xx — сервер сломался, можно повторить.
500 Internal Server Error— что-то упало на стороне сервера.502 Bad Gateway— прокси/балансировщик не получил ответ от backend.503 Service Unavailable— сервис перегружен/в дауне.504 Gateway Timeout— backend не ответил в срок.
Правило для ретраев: повторяем 5xx, 429, network errors. Не повторяем остальные 4xx. Подробно разберём в уроке 04.
200 OK ещё не значит, что в теле то, что вы ждали. Некоторые API на бизнес-ошибки тоже возвращают 200 с полем {"error": "..."} в JSON. Не доверяйте только статус-коду — всегда проверяйте структуру тела.
Заголовки, важные для DE
Заголовки — это пары Имя: значение после стартовой строки. Их сотни, но в DE-работе постоянно встречаются с десяток.
В запросе:
Authorization: Bearer eyJ...— токен для аутентификации. Самый частый формат — Bearer (OAuth/JWT).Authorization: Basic dXNlcjpwYXNz— basic auth, base64(user:password). Только поверх HTTPS.Accept: application/json— клиент готов принять JSON. Многие API возвращают разный формат в зависимости от Accept.Content-Type: application/json— формат тела запроса. Обязателен при POST с JSON.User-Agent: my-etl/1.0— кто стучится. Некоторые API (GitHub) требуют User-Agent.If-Modified-Since: <date>/If-None-Match: <etag>— кэширование, чтобы получить 304 вместо повторного скачивания.
В ответе:
Content-Type: application/json; charset=utf-8— что лежит в теле.Content-Length: 12345— длина тела в байтах.Cache-Control: max-age=3600— сколько секунд ответ можно кэшировать.ETag: "abc123"— версия ресурса для условного запроса.Retry-After: 30— сколько секунд подождать перед повтором (приходит с 429 или 503).Link: <...>; rel="next"— pagination (разберём в уроке 03).X-RateLimit-Limit: 5000/X-RateLimit-Remaining: 4998— кастомные заголовки rate-limit. Не стандарт, у каждого API свой набор.
# Пример: запрос к GitHub с правильными заголовками
import httpx
response = httpx.get(
"https://api.github.com/repos/python/cpython",
headers={
"Authorization": "Bearer ghp_xxxxxxxxxxxxxxxxxxxx",
"Accept": "application/vnd.github+json",
"User-Agent": "my-etl/1.0",
},
timeout=10,
)
print(response.status_code)
print(response.headers["x-ratelimit-remaining"])
print(response.json()["stargazers_count"])
Никакого реального кода с requests/httpx пока не пишем — это следующий урок. Здесь важно понять, что заголовки — это не магия, а просто строки, которые клиент посылает рядом с URL.
TLS и HTTPS
http:// — открытый трафик, любой в сети между вами и сервером может прочитать. https:// — то же самое поверх
Когда вы делаете httpx.get("https://api.github.com/..."), под капотом происходит:
- TCP-соединение на 443 порт.
- : сервер присылает сертификат, клиент проверяет, что он подписан довереннымTLS handshake.Certificate Authority
- Договорились о ключах — дальше всё шифруется.
- Поверх этого работает обычный HTTP.
Самая частая ошибка junior’а:
# КАТЕГОРИЧЕСКИ НЕ ДЕЛАТЬ В PRODUCTION
response = httpx.get("https://api.example.com/data", verify=False)
verify=False отключает проверку сертификата. Это значит, что любой провайдер/прокси/злоумышленник в середине может перехватить ваш трафик, подсунуть свой сертификат и читать ваши токены/данные открытым текстом. На корпоративных DE-задачах за verify=False в проде увольняют. Если CI кричит про сертификат — настоящая проблема в certifi, корп-прокси или системных trust-store, а не в проверке.
Легитимные случаи verify=False: только локальное тестирование против self-signed сервера, который вы сами подняли. В коде, который пойдёт в git/прод — никогда.
Tools для exploration
Перед тем как писать Python-код, всегда проверьте API руками. Это сэкономит часы отладки.
curl — стандартная утилита, есть везде:
curl -i https://api.github.com/repos/python/cpython \
-H "Authorization: Bearer ghp_xxxxxxxxxxxxxxxxxxxx" \
-H "Accept: application/vnd.github+json" \
-H "User-Agent: my-etl/1.0"
Флаг -i печатает заголовки ответа. Флаг -v показывает весь TLS-handshake и обмен сообщениями — полезно, когда не понятно, что не так.
httpie — то же, но с человеческим интерфейсом и подсветкой:
http GET https://api.github.com/repos/python/cpython \
Authorization:"Bearer ghp_xxxx" \
Accept:application/vnd.github+json
Ставится через uv tool install httpie. Для exploration удобнее curl’а в разы.
Postman / Insomnia — GUI-клиенты. Удобно для сложных запросов с многими параметрами, для коллекций запросов, для шаринга с командой. В DE-работе используется реже, чем curl/httpie.
Browser DevTools → вкладка Network. Если API доступен и через UI приложения — открываете в браузере, делаете действие, смотрите, какой запрос ушёл. Часто это самый быстрый способ разобраться в недокументированном API.
Что мы получили
- HTTP — это текстовый диалог: запрос (метод + URL + headers + body) и ответ (status + headers + body).
- Методы делятся на идемпотентные (GET, PUT, DELETE) и нет (POST). От этого зависит, можно ли retry’ить.
- Статусы группируются по первой цифре: 2xx — успех, 3xx — редирект, 4xx — клиент виноват, 5xx — сервер.
- Retry’им только 5xx, 429 и network errors. Остальные 4xx — повторять бессмысленно.
- Заголовки
Authorization,Accept,Content-Type,User-Agent,Retry-After— то, что встречается на каждом DE-проекте. - HTTPS = HTTP + TLS. Никогда не отключайте
verify. - Перед кодом — пробуйте API в curl/httpie. Сэкономите часы.
В следующем уроке возьмём requests и httpx, увидим Session, timeouts, raise_for_status — и напишем первый production-ready клиент к GitHub API.