Обработка кодов состояния HTTP в requests: руководство с примерами
Работа с кодами ответов HTTP в библиотеке requests
Библиотека requests в Python предоставляет удобные инструменты для отправки HTTP-запросов. После выполнения запроса сервер возвращает код состояния (status code) и тело ответа. Умение правильно обрабатывать эти коды необходимо для построения надёжных приложений, взаимодействующих с веб-ресурсами.
Какой самый простой и надёжный способ проверить успешность HTTP-ответа в requests?
Наиболее эффективный подход - использовать метод raise_for_status(). Он автоматически вызывает исключение, если код ответа указывает на ошибку (4xx или 5xx). Это избавляет от ручной проверки кода и делает код чище.
import requests
response = requests.get('https://httpbin.org/status/200')
response.raise_for_status()
print('Запрос выполнен успешно')
Python 2 requests (библиотека requests в python 2)
Если сервер вернул код 200–299, исключение не выбрасывается. При коде 404 или 500 возникнет requests.exceptions.HTTPError с текстом ошибки.
# При статусе 200: Запрос выполнен успешно # При статусе 404: requests.exceptions.HTTPError: 404 Client Error: Not Found for url: ...
Python 3 requests (библиотека requests в python 3)
Типичные проблемы:
- Игнорирование вызова raise_for_status() - код продолжает выполняться даже при неудачном ответе, что может привести к ошибкам при обработке данных.
- Перехват слишком общего исключения Exception - лучше ловить конкретные типы, например requests.exceptions.HTTPError.
- Неправильное использование в асинхронном коде (не относится к библиотеке requests, но стоит помнить).
Цель использования: автоматизация проверки кода ответа, уменьшение дублирования кода, получение понятных сообщений об ошибках.
Как проверить код ответа вручную, без использования raise_for_status?
Иногда требуется более гибкая обработка - например, для разных кодов выполнять разные действия. В этом случае можно напрямую обратиться к атрибуту status_code объекта Response.
import requests
resp = requests.get('https://httpbin.org/status/201')
if resp.status_code == 201:
print('Ресурс создан')
elif resp.status_code == 200:
print('Успешно')
else:
print(f'Неожиданный код: {resp.status_code}')
Python requests url (выполнение запроса по url с помощью requests в python)
Возможные ошибки:
- Сравнение кода с целым числом без учёта диапазона (например, проверка только на 200, а не на 2xx).
- Забывание обработать коды редиректов (3xx), если выключен авторедирект.
Случаи использования: необходимость точной реакции на определённые коды (например, 201 Created, 409 Conflict), обработка пагинации или ожидание определённого состояния.
Как быстро узнать, был ли запрос успешным, без сравнения с конкретными числами?
Атрибут ok возвращает True, если код ответа меньше 400. Это удобная сокращённая проверка.
resp = requests.get('https://httpbin.org/status/200')
if resp.ok:
print('Запрос выполнен')
else:
print('Произошла ошибка')
Python requests headers (заголовки запросов в python requests)
Нюансы:
- Коды 3xx (редиректы) считаются успешными, если включен авторедирект (по умолчанию включён). Если редиректы отключены, код 301, 302 и т.д. будут считаться неуспешными (ok вернёт False).
- Не даёт информации о типе ошибки - только общий статус успеха.
Как обработать все возможные коды ответа с разными действиями?
Можно построить словарь или использовать условные конструкции для каждого значимого кода. Вот пример с обработкой нескольких типичных кодов:
import requests
from http import HTTPStatus
resp = requests.get('https://httpbin.org/status/404')
status_code = resp.status_code
if status_code == HTTPStatus.OK:
print('Успех (200)')
elif status_code == HTTPStatus.CREATED:
print('Создано (201)')
elif status_code == HTTPStatus.NO_CONTENT:
print('Нет содержимого (204)')
elif status_code == HTTPStatus.BAD_REQUEST:
print('Ошибка в запросе (400)')
elif status_code == HTTPStatus.NOT_FOUND:
print('Не найдено (404)')
elif status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
print('Ошибка сервера (500)')
else:
print(f'Код: {status_code}')
Python requests exceptions (исключения в python requests)
Проблемы:
- Код становится громоздким при большом количестве вариантов.
- Легко пропустить какой-либо код, особенно из диапазона 2xx или 4xx.
Цель: точная логика для каждого кода, например, при работе с REST API, где разные коды означают разные состояния.
Что делать, если нужно получить текст сообщения об ошибке вместе с кодом?
Объект Response содержит поля reason (краткое описание) и text (тело ответа). Для ошибок 4xx и 5xx сервер часто возвращает расшифровку.
resp = requests.get('https://httpbin.org/status/400')
print(f'Код: {resp.status_code}, причина: {resp.reason}')
print(f'Тело: {resp.text}')
Python requests codes (коды ответов http в python requests)
Код: 400, причина: Bad Request
Тело: "{'error': '...'}"
Ошибки:
- Попытка распарсить JSON тела ответа без проверки Content-Type - может вызвать исключение, если ответ не в JSON.
- Забывание, что resp.text может быть пустым при некоторых кодах (например, 204 No Content).
Использование: для логирования или отображения пользователю информативного сообщения об ошибке.
Расширенные примеры работы с кодами ответов HTTP
Пример 1: Обработка ошибок с raise_for_status и кастомное исключение
import requests
from requests.exceptions import HTTPError
def safe_get(url):
try:
resp = requests.get(url, timeout=5)
resp.raise_for_status()
return resp.json()
except HTTPError as e:
print(f'HTTP ошибка: {e.response.status_code} - {e.response.reason}')
if e.response.status_code == 404:
print('Ресурс не найден, возвращаем пустой словарь')
return {}
raise
except requests.exceptions.RequestException as e:
print(f'Ошибка подключения: {e}')
return None
data = safe_get('https://httpbin.org/status/404')
print(f'Данные: {data}')
HTTP ошибка: 404 - Not Found
Ресурс не найден, возвращаем пустой словарь
Данные: {}
В этом примере перехватывается HTTPError, для 404 возвращается пустой словарь вместо исключения. Другие ошибки пробрасываются дальше. Также обрабатываются общие ошибки подключения.
Пример 2: Проверка кода ответа и парсинг JSON с разными результатами
import requests
resp = requests.get('https://httpbin.org/uuid')
if resp.status_code == 200:
uuid_data = resp.json()
print(f'UUID: {uuid_data["uuid"]}')
elif resp.status_code == 429:
print('Слишком много запросов. Подождите.')
else:
print(f'Необработанный код: {resp.status_code}')
UUID: 123e4567-e89b-12d3-a456-426614174000
Код реагирует на разные коды: при 200 - парсинг и использование данных, при 429 - сообщение о лимите, иначе - просто вывод кода.
Пример 3: Использование статус-кодов для проверки наличия ресурса (HEAD-запрос)
import requests
def resource_exists(url):
try:
resp = requests.head(url, allow_redirects=True)
return resp.status_code == 200
except requests.exceptions.RequestException:
return False
check = resource_exists('https://httpbin.org/get')
print(f'Существует: {check}')
Существует: True
HEAD-запрос не загружает тело ответа, только заголовки. Код 200 указывает на существование ресурса. allow_redirects=True позволяет следовать редиректам.
Пример 4: Имитация разных кодов с помощью httpbin и тестирование логики
import requests
test_urls = [
'https://httpbin.org/status/200',
'https://httpbin.org/status/201',
'https://httpbin.org/status/400',
'https://httpbin.org/status/500',
'https://httpbin.org/status/302'
]
for url in test_urls:
resp = requests.get(url, allow_redirects=False) # отключаем авторедирект
print(f'URL: {url} -> Код: {resp.status_code}, ok: {resp.ok}, причина: {resp.reason}')
URL: https://httpbin.org/status/200 -> Код: 200, ok: True, причина: OK URL: https://httpbin.org/status/201 -> Код: 201, ok: True, причина: Created URL: https://httpbin.org/status/400 -> Код: 400, ok: False, причина: Bad Request URL: https://httpbin.org/status/500 -> Код: 500, ok: False, причина: Internal Server Error URL: https://httpbin.org/status/302 -> Код: 302, ok: False, причина: Found
Обратите внимание: при отключённом авторедиректе код 302 считается неуспешным. Если бы редиректы были включены, requests автоматически перешёл бы по Location и вернул конечный код (обычно 200).
Пример 5: Обработка кода 304 Not Modified с использованием ETag
import requests
url = 'https://httpbin.org/etag/test'
# Первый запрос - получаем ETag
resp1 = requests.get(url)
etag = resp1.headers.get('ETag')
print(f'Первый ответ: {resp1.status_code}, ETag: {etag}')
# Второй запрос с If-None-Match
headers = {'If-None-Match': etag}
resp2 = requests.get(url, headers=headers)
print(f'Второй ответ: {resp2.status_code}') # Должно быть 304
if resp2.status_code == 304:
print('Данные не изменились, используем кеш')
Первый ответ: 200, ETag: 'test' Второй ответ: 304 Данные не изменились, используем кеш
Код 304 означает, что ресурс не изменился, и сервер не передаёт тело. Это полезно для кеширования.
Пример 6: Кастомный обработчик с проверкой на несколько кодов успеха
import requests
SUCCESS_CODES = {200, 201, 202, 204}
def process_response(resp):
if resp.status_code in SUCCESS_CODES:
print(f'Успешный код: {resp.status_code}')
if resp.status_code == 204:
return None
return resp.json()
else:
print(f'Ошибка {resp.status_code}: {resp.text}')
return None
resp = requests.get('https://httpbin.org/status/202')
data = process_response(resp)
print(f'Полученные данные: {data}')
Успешный код: 202 Полученные данные: None (так как httpbin вернул пустое тело, но код 202)
Здесь определён набор кодов, которые считаются успешными. Для 204 (No Content) возвращается None. Для остальных - пытаемся распарсить JSON.