HTTP-запросы в Python
urllib — стандартная библиотека
Модуль urllib входит в стандартную библиотеку и не требует установки:
import urllib.request
import urllib.parse
import json
# Simple GET request
url = "https://jsonplaceholder.typicode.com/posts/1"
with urllib.request.urlopen(url) as response:
data = json.loads(response.read().decode())
print(f"Статус: {response.status}")
print(f"Заголовок: {data['title']}")
# GET with query parameters
params = urllib.parse.urlencode({"userId": 1})
url = f"https://jsonplaceholder.typicode.com/todos?{params}"
with urllib.request.urlopen(url) as response:
todos = json.loads(response.read().decode())
print(f"Найдено: {len(todos)} задач")
# Error handling
from urllib.error import HTTPError, URLError
try:
with urllib.request.urlopen("https://httpbin.org/status/404") as resp:
pass
except HTTPError as e:
print(f"HTTP ошибка: {e.code} {e.reason}")
except URLError as e:
print(f"Ошибка соединения: {e.reason}")
urllib работает, но его API громоздкий. Для реальных проектов используйте requests или httpx.
requests — удобный HTTP-клиент
pip install requests
import requests
# GET request
response = requests.get("https://jsonplaceholder.typicode.com/posts/1")
print(response.status_code) # 200
data = response.json()
print(data["title"])
# GET with parameters
response = requests.get(
"https://jsonplaceholder.typicode.com/posts",
params={"userId": 1},
)
posts = response.json()
print(f"Постов: {len(posts)}")
# POST request
response = requests.post(
"https://jsonplaceholder.typicode.com/posts",
json={"title": "Новый пост", "body": "Содержимое", "userId": 1},
)
print(f"Создан: #{response.json()['id']}")
# Custom headers
response = requests.get(
"https://api.github.com/user",
headers={"Authorization": "Bearer ghp_token_here"},
)
Таймауты и повторные запросы
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# Always set timeout!
try:
response = requests.get("https://httpbin.org/delay/5", timeout=3)
except requests.Timeout:
print("Запрос превысил таймаут")
except requests.ConnectionError:
print("Ошибка соединения")
# Tuple timeout: (connect_timeout, read_timeout)
response = requests.get("https://httpbin.org/get", timeout=(3, 10))
# Session with retries
session = requests.Session()
retries = Retry(total=3, backoff_factor=0.5, status_forcelist=[500, 502, 503])
session.mount("https://", HTTPAdapter(max_retries=retries))
response = session.get("https://httpbin.org/get", timeout=5)
Аутентификация и сессии
import requests
# Basic Auth
response = requests.get(
"https://httpbin.org/basic-auth/user/pass",
auth=("user", "pass"),
)
# Bearer Token
response = requests.get(
"https://api.example.com/data",
headers={"Authorization": "Bearer your_token"},
)
# Session — cookies persist between requests
session = requests.Session()
session.headers.update({"User-Agent": "MyApp/1.0"})
session.post("https://httpbin.org/cookies/set/session_id/abc123")
response = session.get("https://httpbin.org/cookies")
print(response.json())
session.close()
httpx — современная альтернатива
httpx совместим с requests по API, но поддерживает async/await и HTTP/2:
pip install httpx
import httpx
# Synchronous API (like requests)
response = httpx.get("https://jsonplaceholder.typicode.com/posts/1")
print(response.json()["title"])
# With client (connection pooling)
with httpx.Client(
base_url="https://jsonplaceholder.typicode.com",
timeout=10.0,
) as client:
posts = client.get("/posts", params={"userId": 1}).json()
user = client.get("/users/1").json()
print(f"{user['name']}: {len(posts)} постов")
Асинхронные запросы
import asyncio
import httpx
async def fetch_data() -> None:
async with httpx.AsyncClient(
base_url="https://jsonplaceholder.typicode.com",
timeout=10.0,
) as client:
# Concurrent requests
responses = await asyncio.gather(
client.get("/users/1"),
client.get("/posts", params={"userId": 1}),
client.get("/comments", params={"postId": 1}),
)
user = responses[0].json()
posts = responses[1].json()
comments = responses[2].json()
print(f"Пользователь: {user['name']}")
print(f"Постов: {len(posts)}, комментариев: {len(comments)}")
asyncio.run(fetch_data())
Обработка ошибок
import httpx
def safe_api_call(url: str) -> dict | None:
"""Make API call with comprehensive error handling."""
try:
response = httpx.get(url, timeout=5.0)
response.raise_for_status()
return response.json()
except httpx.TimeoutException:
print(f"Таймаут при запросе к {url}")
except httpx.HTTPStatusError as e:
print(f"HTTP ошибка {e.response.status_code}")
except httpx.ConnectError:
print(f"Не удалось подключиться к {url}")
except httpx.RequestError as e:
print(f"Ошибка запроса: {e}")
return None
Практический пример: API-клиент
from dataclasses import dataclass
import httpx
@dataclass
class APIClient:
"""Reusable API client with error handling."""
base_url: str
token: str | None = None
timeout: float = 10.0
def _headers(self) -> dict[str, str]:
headers = {"Accept": "application/json"}
if self.token:
headers["Authorization"] = f"Bearer {self.token}"
return headers
def get(self, path: str, params: dict | None = None) -> dict:
response = httpx.get(
f"{self.base_url}{path}",
params=params,
headers=self._headers(),
timeout=self.timeout,
)
response.raise_for_status()
return response.json()
def post(self, path: str, data: dict) -> dict:
response = httpx.post(
f"{self.base_url}{path}",
json=data,
headers=self._headers(),
timeout=self.timeout,
)
response.raise_for_status()
return response.json()
# Usage
client = APIClient(base_url="https://jsonplaceholder.typicode.com")
posts = client.get("/posts", params={"userId": 1})
new_post = client.post("/posts", data={"title": "Новый", "body": "Текст", "userId": 1})
print(f"Получено {len(posts)} постов, создан #{new_post['id']}")