Python requests: GET и POST запросы, таймаут и ошибки

Категория: Python · Язык: Python · Обновлено:

Библиотека requests отправляет HTTP-запросы одной строкой: requests.get(url, timeout=10) для получения данных и requests.post(url, json=data, timeout=10) для отправки JSON. Всегда указывайте timeout: без него запрос может зависнуть навсегда.

Установка и GET-запрос

Bash
pip install requests
Python
import requests

response = requests.get(
    "https://api.example.com/users",
    params={"page": 2},
    timeout=10,
)
print(response.status_code)       # 200
print(response.url)               # https://api.example.com/users?page=2
print(response.json()["page"])    # 2

Словарь params сам кодирует параметры и добавляет их к адресу. Метод response.json() разбирает тело ответа как JSON.

POST с JSON и с формой

Python
created = requests.post(
    "https://api.example.com/users",
    json={"name": "Анна", "role": "admin"},
    timeout=10,
)
print(created.status_code)        # 201
print(created.json()["name"])     # Анна

form = requests.post("https://api.example.com/login", data={"user": "anna"}, timeout=10)
print(form.status_code)           # 200

Параметр json= преобразует словарь в JSON и ставит заголовок Content-Type: application/json. Параметр data= отправляет обычную форму application/x-www-form-urlencoded.

Заголовки и авторизация

Python
token = "token123"
secured = requests.get(
    "https://api.example.com/secure",
    headers={"Authorization": f"Bearer {token}", "Accept": "application/json"},
    timeout=10,
)
print(secured.status_code)        # 200

denied = requests.get("https://api.example.com/secure", timeout=10)
print(denied.status_code)         # 401

Для базовой авторизации есть параметр auth=("логин", "пароль").

Ошибки и таймаут

Статус 4xx и 5xx сам по себе исключения не вызывает. Его бросает метод raise_for_status():

Python
try:
    bad = requests.get("https://api.example.com/fail", timeout=10)
    bad.raise_for_status()
except requests.HTTPError as error:
    print("ошибка сервера:", error.response.status_code)

try:
    requests.get("https://api.example.com/slow", timeout=1)
except requests.Timeout:
    print("таймаут")

Исключение

Когда возникает

requests.Timeout

Сервер не ответил за указанное время

requests.ConnectionError

Нет соединения, неверный адрес, отказ сервера

requests.HTTPError

raise_for_status() на ответе 4xx или 5xx

requests.RequestException

Базовое исключение, перехватывает все перечисленные

В timeout можно передать пару: timeout=(3, 10) задаёт отдельно время на соединение и на чтение ответа.

Session и автоматические повторы

Сессия переиспользует соединение и заголовки. С адаптером Retry запрос повторяется при временных сбоях:

Python
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
session.headers.update({"User-Agent": "my-app/1.0"})
retry = Retry(total=3, backoff_factor=0.1, status_forcelist=[502, 503, 504])
session.mount("https://", HTTPAdapter(max_retries=retry))
session.mount("http://", HTTPAdapter(max_retries=retry))

flaky = session.get("https://api.example.com/flaky", timeout=10)
print(flaky.status_code)          # 200

Скачать большой файл

Python
with requests.get("https://api.example.com/file", stream=True, timeout=10) as r:
    r.raise_for_status()
    with open("download.bin", "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)

Частые вопросы

Почему нельзя отключать проверку сертификата через verify=False?

Запрос перестаёт защищаться от подмены сервера. Если сертификат внутренний, укажите путь к нему: verify="/path/ca.pem".

Как отправить файл?

Передайте открытый файл в files: requests.post(url, files={"file": open("a.txt", "rb")}, timeout=30).

Чем requests отличается от httpx?

httpx умеет асинхронные запросы и HTTP/2, интерфейс похож. Для простых скриптов requests по-прежнему достаточно.