Menu
Русский

HTTP-запросы в Python: библиотека requests (GET, POST, JSON)

Как делать HTTP-запросы в Python через библиотеку requests — GET, POST, query-параметры, заголовки, JSON-тела и обработка ошибок.

Тянем данные из интернета в пару строк

Большинству реальных Python-программ рано или поздно нужно поговорить с чем-то по сети — REST API, сервисом погоды, эндпойнтом GitHub, скачать файл. Стандартная библиотека это умеет (через urllib), но де-факто инструмент мира Python — сторонняя библиотека requests. Её API настолько приятнее, что один pip install того стоит.

pip install requests

Если ты ещё не работаешь в виртуальном окружении — сначала создай его: это удержит установку в пределах проекта.

Первый GET-запрос

main.py
Output
Click Run to see the output here.

Три строки: вызов get, проверка статус-кода, взгляд на тело. response.text — тело ответа строкой. Здесь обрезано: полный JSON длинный.

Статус-коды, которые стоит узнавать в лицо:

  • 200 — OK, всё сработало.
  • 201 — Created (типичный ответ на POST).
  • 301 / 302 — редиректы; requests по умолчанию автоматически их следует.
  • 400 — плохой запрос; что-то в отправленном не так.
  • 401 / 403 — не аутентифицирован / нет прав.
  • 404 — ресурс не существует.
  • 429 — rate limit; замедлись.
  • 500 — ошибка сервера.

Парсинг JSON-ответа

Когда эндпойнт возвращает JSON — вызывай .json() на ответе: он распарсит тело и отдаст тебе словарь (или список):

main.py
Output
Click Run to see the output here.

Под капотом .json() — то же самое, что json.loads(response.text), просто сокращение для типичного случая.

Query-параметры

Не склеивай ?key=value&... к URL руками. Передавай словарь в params=:

main.py
Output
Click Run to see the output here.

requests сам кодирует URL — пробелы, спецсимволы, Unicode всё переживают корректно.

Реально ушедший URL доступен в response.url — удобно для отладки.

POST с JSON-телом

Для отправки данных — создание ресурсов, формы, мутирующие эндпойнты — используй requests.post:

import requests

payload = {
    "title": "Docs update",
    "body": "Added HTTP requests page.",
    "labels": ["docs"],
}

response = requests.post(
    "https://api.example.com/issues",
    json=payload,
    headers={"Authorization": "Bearer YOUR_TOKEN"},
)

print(response.status_code)
print(response.json())

Аргумент json= делает две вещи: сериализует payload в JSON и проставляет Content-Type: application/json. Можно сделать то же самое руками через data=json.dumps(payload) и явный заголовок, но json= — идиоматичное сокращение.

Для form-encoded данных (то, что шлёт классическая HTML-форма) используй data=:

requests.post("https://example.com/login", data={"user": "rosa", "password": "..."})

Заголовки

Любые свои заголовки — словарём:

import requests

response = requests.get(
    "https://api.example.com/profile",
    headers={
        "Authorization": "Bearer abc123",
        "User-Agent": "my-tool/1.0",
    },
)

Большинство API хотят заголовок Authorization для аутентификации. Конкретная схема (Bearer, Basic, Token) — в их документации.

Таймаут не опционален

По умолчанию requests будет ждать ответа вечно. В реальной программе это превращает флейкующий сервер в подвисший скрипт. Всегда передавай timeout:

import requests

try:
    response = requests.get("https://api.example.com/slow", timeout=5)
except requests.Timeout:
    print("Server took too long.")

Число — секунды. timeout=5 — «сдавайся, если за 5 секунд ответа нет». Можно передать кортеж (connect_timeout, read_timeout) для большего контроля.

Обработка ошибок

Бывают две категории проблем:

  1. Ошибки уровня HTTP (4xx, 5xx) — сервер ответил, но ответ — ошибка. response.status_code скажет.
  2. Сетевые проблемы — таймауты, DNS, недоступные хосты. Они поднимают исключения.

Идиоматичный паттерн сочетает оба:

main.py
Output
Click Run to see the output here.

raise_for_status() для 2xx-ответов — no-op, для остальных — поднимает исключение. RequestException — базовый класс для каждой ошибки, поднимаемой requests: единая ловушка для «что-то пошло не так при разговоре с эндпойнтом».

Скачиваем файл

Для больших бинарных загрузок — стримь ответ, чтобы он не сидел в памяти:

import requests

url = "https://example.com/large.zip"

with requests.get(url, stream=True, timeout=30) as r:
    r.raise_for_status()
    with open("large.zip", "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)

stream=True говорит requests не предзагружать тело. iter_content(chunk_size=...) отдаёт тело кусками, и ты пишешь их сразу на диск.

Сессии: переиспользование соединений и дефолтов

Если собираешься делать несколько запросов к одному сервису, используй Session. Он переиспользует нижележащее TCP-соединение (быстрее) и позволяет один раз задать дефолты:

import requests

session = requests.Session()
session.headers.update({"Authorization": "Bearer abc123"})

# Every request through this session carries the header.
a = session.get("https://api.example.com/users/1")
b = session.get("https://api.example.com/users/2")
c = session.post("https://api.example.com/users", json={"name": "Rosa"})

Для скриптов, бьющих в один и тот же API десятки раз, сессия даёт заметный прирост.

Реалистичный пример: маленький GitHub-клиент

Получаем последний релиз репозитория:

import requests

def latest_release(owner, repo):
    url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    data = response.json()
    return {
        "tag": data["tag_name"],
        "name": data["name"],
        "published": data["published_at"],
        "url": data["html_url"],
    }

release = latest_release("python", "cpython")
print(release)

Меньше пятнадцати строк: построил URL, запросил, проверил на ошибки, достал интересующие поля. Такую форму принимают большинство API-клиентов, которые ты будешь писать.

А что насчёт urllib?

urllib.request из стандартной библиотеки умеет всё, что requests, — в большем числе строк и с худшей эргономикой. Если добавить зависимость никак нельзя — вот:

import json
import urllib.request

with urllib.request.urlopen("https://api.github.com/repos/python/cpython") as r:
    data = json.loads(r.read().decode("utf-8"))

print(data["name"])

Для чего-то посерьёзнее быстрого скрипта requests (или httpx, если нужно async) стоит установки.

Несколько привычек

  • Всегда ставь таймаут. Без исключений.
  • Используй raise_for_status() в скриптах, ожидающих успеха, — превратит плохой ответ в громкое исключение.
  • Передавай словари в params= и json=, а не склеивай строки руками.
  • Заворачивай связанные вызовы в Session, когда много раз ходишь в один API.
  • Логируй response.status_code и response.text при отладке — тело обычно прямо говорит, что сервер не так понял.

Дальше: даты и время

С requests в арсенале ты умеешь говорить с любым современным веб-API, качать файлы и строить маленькие интеграции между сервисами. В связке со страницами про JSON и CSV у тебя теперь полный цикл «забрать данные, прочесть, что-то с ними сделать, записать обратно» — форма огромного числа реальных Python-скриптов. У большинства этих данных есть временная метка, и следующая страница — о том, как Python представляет даты, время и каких часовых ловушек стоит избегать.

Часто задаваемые вопросы

Как сделать HTTP-запрос в Python?

Установи библиотеку requests через pip install requests, затем вызови requests.get(url) для GET или requests.post(url, json=...) для POST. У объекта ответа есть .status_code, .text, .json() и .headers. Пример: r = requests.get('https://api.example.com/users/1').

requests или urllib?

requests — для всего, что ты пишешь руками: его API значительно дружелюбнее. urllib встроен и нормально, когда добавить зависимость нельзя, но на JSON-тела и сессии нужно больше кода. В продакшене многие команды используют ещё httpx — requests-совместимую библиотеку с async-поддержкой.

Как послать JSON в POST-запросе?

Передай json={'key': 'value'} в requests.post(...). requests сам сериализует словарь в JSON и проставит Content-Type: application/json. Не передавай одновременно data= и json= — выбери одно.

Как обрабатывать ошибки с requests?

Проверь response.status_code (200 — успех) или вызови response.raise_for_status(), чтобы поднять исключение на 4xx/5xx. Сетевые проблемы (таймауты, падения DNS) поднимают наследников requests.RequestException — лови его, чтобы покрыть всё.

Учитесь программировать с Coddy

НАЧАТЬ