Изучаем сетевые библиотеки Python: от простых запросов до асинхронных решений
Сетевые библиотеки Python: обзор и практическое применение
Сетевые взаимодействия - одна из ключевых задач в современной разработке. Python предлагает несколько встроенных и сторонних библиотек, позволяющих выполнять HTTP-запросы, работать с сокетами, реализовывать асинхронные соединения. В этой части рассмотрены основные варианты, их цели и типичные ошибки.
Библиотека requests - универсальное решение для HTTP
Библиотека requests является де‑факто стандартом для синхронных HTTP-запросов в Python. Она предоставляет удобный API, автоматическое управление куками, сессиями и обработку ошибок.
import requests
# GET-запрос
response = requests.get('https://api.github.com', timeout=5)
print(response.status_code, response.json().get('current_user_url'))
Python библиотеки словари (библиотеки для работы со словарями в python)
Пояснение: функция get отправляет GET-запрос. Параметр timeout предотвращает зависание. Метод json() декодирует ответ из JSON.
Частая ошибка: забыть про timeout. При отсутствии ответа от сервера запрос может висеть бесконечно. Решение - всегда указывать тайм-аут.
Ошибка SSL‑сертификата: requests.exceptions.SSLError. Решение - проверить сертификаты или временно отключить верификацию (verify=False), но делать это стоит только в отладочных целях.
Цель использования: быстрая отправка запросов к REST API, скачивание веб‑страниц, взаимодействие с микросервисами.
Вариант 1: Как создать низкоуровневое TCP‑соединение с помощью socket?
Модуль socket входит в стандартную библиотеку и даёт полный контроль над передачей данных по протоколам TCP и UDP.
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('httpbin.org', 80))
sock.sendall(b'GET /get HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n')
response = sock.recv(4096)
print(response.decode())
sock.close()
библиотека python user (пользовательские библиотеки python)
Пояснение: создаётся TCP‑сокет, устанавливается соединение, отправляется сырой HTTP‑запрос, читается ответ. После использования сокет закрывается.
Проблема: возможна потеря данных при неполном чтении. Решение - использовать цикл для получения всех данных до закрытия соединения.
Цель: реализация кастомных протоколов, тестирование, работа с сырыми данными.
Вариант 2: Как использовать встроенную urllib для HTTP?
Модуль urllib (часть стандартной библиотеки) позволяет выполнять HTTP‑запросы без установки сторонних пакетов.
from urllib.request import urlopen
from urllib.parse import urlencode
params = urlencode({'key1': 'value1'})
with urlopen('https://httpbin.org/get?' + params) as response:
data = response.read().decode()
print(data)
библиотека алгоритмов python (библиотека алгоритмов в python)
Пояснение: urlencode формирует строку запроса, urlopen открывает соединение, возвращает файлоподобный объект.
Ошибка: сложность работы с заголовками и методами (POST требует отдельных шагов). Решение - использовать urllib.request.Request для тонкой настройки.
Цель: простые запросы без установки зависимостей, например, в скриптах для администрирования.
Вариант 3: Как реализовать асинхронные HTTP‑запросы с aiohttp?
Библиотека aiohttp предназначена для асинхронного ввода‑вывода с использованием asyncio. Это идеальный выбор для высоконагруженных приложений.
import aiohttp
import asyncio
async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.json()
async def main():
data = await fetch('https://api.github.com')
print(data.get('current_user_url'))
asyncio.run(main())
библиотека скриптов python (библиотека скриптов в python)
Пояснение: используется контекстный менеджер ClientSession для управления соединениями. Ключевое слово await приостанавливает корутину до получения ответа.
Типичная ошибка: забыть установить asyncio.run для запуска асинхронной функции. Без этого код не выполнится.
Цель: обработка множества запросов одновременно (веб‑скрапинг, микросервисы), где важна производительность.
Вариант 4: Как работать с httpx, поддерживающим синхронный и асинхронный режимы?
httpx - современная библиотека, объединяющая удобство requests и асинхронность aiohttp. Поддерживает HTTP/2, повторные попытки, клиентские сессии.
import httpx
# Синхронный режим
with httpx.Client() as client:
response = client.get('https://httpbin.org/get', params={'key': 'value'})
print(response.json())
# Асинхронный режим
async def async_example():
async with httpx.AsyncClient() as client:
response = await client.get('https://httpbin.org/get')
print(response.status_code)
Пояснение: Client и AsyncClient позволяют переиспользовать соединения. Параметры передаются через params.
Проблема: для асинхронного режима требуется среда asyncio. В синхронных программах случайное использование AsyncClient приведёт к ошибке. Решение - явно выбирать режим в зависимости от задачи.
Цель: универсальное решение для проектов, где может понадобиться переключение между синхронным и асинхронным кодом.
Расширенные примеры работы с сетевыми библиотеками
1. Сессии, повторные попытки и тайм-ауты в requests
from requests import Session, RequestException
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = Session()
retries = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
adapter = HTTPAdapter(max_retries=retries)
session.mount('https://', adapter)
session.mount('http://', adapter)
try:
resp = session.get('https://httpbin.org/status/500', timeout=10)
resp.raise_for_status()
except RequestException as e:
print(f"Ошибка: {e}")
finally:
session.close()
# При выполнении будет выброшено исключение после всех попыток (если ошибка не исправилась). Вывод в консоль: # Ошибка: 500 Server Error: INTERNAL SERVER ERROR for url: https://httpbin.org/status/500
Пояснение: создаётся сессия с политикой повторных попыток (3 попытки, задержка по экспоненте). Адаптер монтируется к префиксам протоколов. Тайм-аут гарантирует, что запрос не зависнет.
2. Асинхронное скачивание нескольких файлов с aiohttp
import aiohttp
import asyncio
from pathlib import Path
async def download_file(session, url, filename):
async with session.get(url) as resp:
if resp.status == 200:
content = await resp.read()
Path(filename).write_bytes(content)
return f"Скачан {filename}"
return f"Ошибка {resp.status} для {filename}"
async def main():
urls = [
("https://httpbin.org/image/png", "image1.png"),
("https://httpbin.org/image/jpeg", "image2.jpg")
]
async with aiohttp.ClientSession() as session:
tasks = [download_file(session, url, name) for url, name in urls]
results = await asyncio.gather(*tasks)
for res in results:
print(res)
asyncio.run(main())
# Вывод: # Скачан image1.png # Скачан image2.jpg
Пояснение: asyncio.gather запускает несколько корутин параллельно. Каждая корутина скачивает файл и сохраняет его в локальную папку.
3. Работа с WebSocket через websockets и asyncio
import asyncio
import websockets
async def echo():
uri = "wss://echo.websocket.org"
async with websockets.connect(uri) as websocket:
await websocket.send("Hello, World!")
response = await websocket.recv()
print(f"Получено: {response}")
asyncio.run(echo())
# Вывод: # Получено: Hello, World!
Пояснение: библиотека websockets упрощает работу с WebSocket. После установки соединения отправляется сообщение и ожидается ответ (эхо-сервер возвращает то же самое).
4. Использование низкоуровневых сокетов для простого UDP-чата
import socket
# Сервер
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8888))
print("Сервер слушает порт 8888...")
data, addr = server.recvfrom(1024)
print(f"Получено от {addr}: {data.decode()}")
server.sendto('Привет от сервера'.encode(), addr)
server.close()
# (при отправке клиентом сообщения "Привет")
# Сервер слушает порт 8888...
# Получено от ('127.0.0.1', 54321): Привет
Пояснение: UDP-сокет не требует установки соединения. Сервер принимает одно сообщение, отвечает и закрывается. Для полноценного чата потребуется цикл.
5. Потоковая передача данных с httpx (загрузка больших файлов)
import httpx
with httpx.stream('GET', 'https://speed.hetzner.de/100MB.bin') as response:
with open('large_file.bin', 'wb') as f:
for chunk in response.iter_bytes(chunk_size=8192):
f.write(chunk)
print("Файл скачан")
Пояснение: httpx.stream позволяет получать ответ чанками, не загружая его целиком в память. Это необходимо для больших файлов или потокового аудио/видео.