В современной разработке взаимодействие с API и веб-сервисами — это ежедневная задача. Умение правильно формировать, отлаживать и автоматизировать HTTP-запросы является базовым навыком. В этой статье мы подробно разберем основные инструменты: консольную утилиту cURL, популярную библиотеку Python Requests, а также современные средства автоматической конвертации между ними.
Основы HTTP-запросов: что нужно знать
Любой HTTP-запрос состоит из нескольких ключевых компонентов. Понимание их роли критически важно для успешной интеграции:
- Метод (Verb): GET, POST, PUT, PATCH, DELETE и др. Определяет действие над ресурсом.
- URL (Endpoint): Адрес ресурса, к которому обращаемся (схема, хост, путь, query-параметры).
- Заголовки (Headers): Метаданные запроса:
Content-Type,Authorization,User-Agent,Accept,Cookieи кастомные заголовки API. - Тело запроса (Body/Payload): Данные, отправляемые на сервер (JSON, Form Data, Multipart, бинарные файлы). Обычно используется с POST/PUT/PATCH.
- Параметры строки запроса (Query Params): Ключ-значение в URL после знака
?(фильтрация, пагинация, токены).
cURL: стандарт де-факто для командной строки
cURL (Client URL) — это мощнейший инструмент для передачи данных по сети. Он доступен в Linux, macOS и Windows (начиная с Windows 10 build 17063).
Базовый синтаксис
curl [опции] [URL] Часто используемые флаги
| Флаг | Описание |
|---|---|
-X, --request | Указать HTTP-метод (GET, POST, PUT…) |
-H, --header | Добавить (можно многократно) |
-d, --data | Данные для отправки в теле (включает POST автоматически) |
--data-urlencode | Данные с URL-кодированием (для application/x-www-form-urlencoded) |
-F, --form | Отправка multipart/form-data (файлы, формы) |
-u, --user | Basic Auth (user:password) |
-b, --cookie | Отправить куки |
-c, --cookie-jar | Сохранить куки в файл |
-v, --verbose | Подробный вывод (заголовки запроса/ответа, handshake) |
-s, --silent | Тихий режим (скрыть прогресс-бар) |
-S, --show-error | Показывать ошибки даже в silent режиме |
-L, --location | Следовать редиректам (3xx) |
--compressed | Запросить сжатый ответ (gzip, deflate, br, zstd) |
--fail-with-body | Возвращать код ошибки при 4xx/5xx, но выводить тело ответа |
--cert / --key | Клиентские сертификаты для mTLS |
Примеры реальных сценариев
GET запрос с авторизацией и параметрами
curl -sS -L --compressed
-H "Authorization: Bearer *"
-H "Accept: application/json"
"https://api.example.com/v1/resources?limit=10&sort=desc" POST JSON с кастомными заголовками
curl -X POST "https://api.example.com/v1/orders"
-H "Content-Type: application/json"
-H "X-Request-ID: "
-d '{"product_id": 123, "quantity": 2, "metadata": {"source": "web"}}' Отправка файла (multipart/form-data)
curl -X POST "https://api.example.com/upload"
-H "Authorization: Bearer "
-F "file=@/path/to/document.pdf"
-F "description=Monthly report" Сложный пример из практики (браузероподобный запрос)
Часто приходится копировать запрос из DevTools (Copy as cURL). Такой запрос содержит множество заголовков для имитации браузера:
curl "https://www.python.org/"
--compressed
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0" -H "Accept-Language: en-US,en;q=0.5"
-H "Accept-Encoding: gzip, deflate, br, zstd"
-H "Referer: https://www.google.com/"
-H "Connection: keep-alive"
-H "Cookie: _ga=*; session_id=*"
-v Python Requests: «HTTP для людей»
Библиотека requests, де-факто стандарт для HTTP в Python. Установка: pip install requests.
Базовая структура
import requests
response = requests.request(
method="GET",
url="https://api.example.com/data",
params={"key": "value"}, # Query parameters
headers={"Authorization": "Bearer *"},
data=None, # Body для form/data или bytes
json=None, # Автоматически делает JSON и ставит Content-Type
files=None, # Multipart files
auth=("user", "pass"), # Basic Auth
cookies={"session": "*"},
timeout=30, # Критически важно!
allow_redirects=True,
verify=True, # SSL verification (не ставьте False в проде!)
cert=("/path/client.crt", "/path/client.key") # mTLS
)
print(response.status_code)
print(response.headers)
print(response.json) # или .text, .content Типичные ошибки при миграции с cURL
- Параметр
datavsjson:- В cURL:
-d '{"a":1}' -H "Content-Type: application/json" - В Requests: используйте
json={"a": 1}. Библиотека сама сериализует и выставит . Если передать строку вdata,Content-Typeнужно ставить вручную.
- В cURL:
- Файлы (
-Fvsfiles):- cURL:
-F "file=@test.txt" - Requests:
files={"file": open("test.txt", "rb")}. Обратите внимание: если в cURL файл передавался через-d @file.json(как raw body), в Requests этоdata=open("file.json", "rb").read, а неfiles.
- cURL:
- URL-кодировка (
--data-urlencode): В Requests передавайте словарь вdata(для form) илиparams(для query). - User-Agent: Requests по умолчанию шлет
python-requests/2.x.x. Многие API блокируют его. Всегда задавайте свой:headers={"User-Agent": "MyApp/1.0"}. - Редиректы: cURL по умолчанию не следует за редиректами (нужен
-L). Requests следует по умолчанию (allow_redirects=True). - SSL/TLS: cURL часто прощает самоподписанные сертификаты с
-k. В Requestsverify=False(небезопасно) илиverify="/path/to/ca_bundle.pem".
Автоматизация конвертации: CurlFetch2Py
Ручное переписывание сложных cURL-команд (с десятками заголовков, куками, multipart-данными) чревато ошибками. На помощь приходит библиотека CurlFetch2Py.
Установка и базовое использование
pip install curl_fetch2py from curl_fetch2py import CurlFetch2Py
converter = CurlFetch2Py
curl_command = '''
curl "https://api.topvisor.ru/v2/json/get/bank/history"
-H "Content-Type: application/json"
-H "User-Id: "
-H "Authorization: Bearer "
-d '{"fields": ["info", "sum", "date"], "orders": [{"name": "date", "direction": "DESC"}], "limit": 10}'
'''
parsed = converter.parse_curl_context(curl_command)
python_code = converter.to_requests_code(parsed)
print(python_code) Что делает парсер за вас
- Правильно определяет метод (POST, если есть
-d/-F, иначе GET). - Парсит все заголовки (
-H), нормализует ключи. - Разбирает тело: JSON, Form Data, Multipart (файлы через
@). - Извлекает куки из
-b/--cookie. - Обрабатывает аутентификацию (
-u). - Поддерживает Fetch API формат (JS
fetch(...)) через методparse_fetch_context.
Пример результата генерации
import requests
url = "https://api.topvisor.ru/v2/json/get/bank/history"
headers = {
"Content-Type": "application/json",
"User-Id": "",
"Authorization": "Bearer ",
}
json_data = {
"fields": ["info", "sum", "date"],
"orders": [{"name": "date", "direction": "DESC"}],
"limit": 10,
}
response = requests.post(url, headers=headers, json=json_data, timeout=30)
print(response.status_code)
print(response.json) Отладка и диагностика: «Почему не работает?»
Даже с идеальным кодом запросы могут падать. Алгоритм диагностики:
- Сравните «на проводах»: Используйте Wireshark, mitmproxy или Fiddler. Сравните байт-в-байт запрос из cURL (рабочий) и из Python (нерабочий). Обратите внимание на:
- Порядок заголовков (редко важно, но бывает).
- Наличие/отсутствие
Content-LengthvsTransfer-Encoding: chunked. - Точное содержимое тела (пробелы, переносы строк, кодировка UTF-8).
- Проверьте редиректы: Выведите
response.historyв Python. cURL без-Lостановится на 301/302, а Requests уйдет дальше, возможно, теряя заголовкиAuthorizationпри смене хоста. - User-Agent и бот-детекция: Если сервер возвращает 403/Cloudflare challenge — поставьте реальный User-Agent и, при необходимости, имитируйте TLS-фингерпринт (библиотека
curl_cffiилиhttpxсhttp2=True). - Кодировка тела: При отправке JSON через
data=json_stringубедитесь, что строка в UTF-8 и не содержит BOM. Лучше всегда использовать параметрjson=dict_obj. - Куки и сессии: Для цепочек запросов используйте
requests.Session— он автоматически хранит куки и connection pooling.
Продвинутые темы и лучшие практики
Асинхронность (async/await)
Для высоконагруженных задач используйте httpx или aiohttp. Они поддерживают тот же API, что и requests, но асинхронны.
import httpx
async def fetch_all(urls):
async with httpx.AsyncClient(timeout=30.0, headers={"User-Agent": "MyBot/1.0"}) as client:
tasks = [client.get(url) for url in urls]
responses = await asyncio.gather(*tasks)
return responses Retry и Backoff
Сеть ненадежна. Настройте повторные попытки с экспоненциальной задержкой.
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["HEAD", "GET", "OPTIONS", "POST"] # POST с идемпотентностью!
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session = requests.Session
session.mount("https://", adapter)
session.mount("http://", adapter) Безопасность: секреты не в коде
Никогда не хардкодьте токены, API-ключи, пароли.
- Используйте переменные окружения (
os.getenv("API_TOKEN")). - В продакшене: Vault, AWS Secrets Manager, Doppler, .env файлы (только для дева).
- В примерах выше чувствительные данные заменены на
*.
mTLS (Mutual TLS)
Если API требует клиентский сертификат:
# cURL
curl --cert client.crt --key client.key "https://secure.api.com"
requests.get("https://secure.api.com", cert=("client.crt", "client.key"), verify="ca_bundle.pem") Чек-лист перед отправкой в прод
- [ ] Установлен адекватный
timeout(connect + read). - [ ] Обработаны коды ошибок (4xx, 5xx) через
response.raise_for_statusили ручную проверку. - [ ] Логируются: URL, метод, статус код, время ответа, ID запроса (есть в заголовках), НЕ логируются тела с секретами/ПДн.
- [ ] Используется
Sessionдля пулинга соединений. - [ ] Реализованы Retry с идемпотентностью (не ретраить платежи дважды без ключа идемпотентности).
- [ ] Проверена работа за корпоративным прокси (переменные
HTTP_PROXY,HTTPS_PROXYили параметрproxiesв requests). - [ ] Тесты на моках (responses, httpretty, pytest-httpx) и интеграционные тесты на стенде.
Отправка HTTP-запросов — это не просто «вызов функции». Это работа с протоколом, сериализацией, безопасностью, отказоустойчивостью и наблюдаемостью. Начните с cURL для исследования и отладки, автоматизируйте рутину конвертации через CurlFetch2Py, пишите продакшн-код на requests (или httpx для асинхронности), всегда используйте таймауты и ретраи, и никогда не храните секреты в репозитории.
Помните: лучший запрос — это тот, который вы можете воспроизвести, отладить и объяснить коллеге через полгода.