Перейти к содержанию

Руководство · На русском

Прокси в aiohttp: авторизация и таймауты

Подключите асинхронный Python-клиент через HTTP-прокси и проверьте один HTTPS-запрос. Пример отделяет авторизацию шлюза от заголовков сайта, ограничивает время ожидания и закрывает сетевые ресурсы.

Инструкции в этом разделе — на русском. Магазин, личный кабинет, документация и условия заказа пока доступны на английском.

Опубликовано:

Короткие ответы

Где указать прокси в aiohttp?

Передайте адрес в proxy= при вызове session.get(). В примере для aiohttp 3.14.3 заголовок авторизации шлюза передаётся через proxy_headers этого запроса. Обычные headers предназначены для сервера назначения.

Почему proxy_headers не работает в ClientSession?

Конструктор ClientSession в проверенной версии 3.14.3 не принимает этот параметр. Он указан на запросе. Проверьте установленную версию, прежде чем смешивать старые и новые примеры настройки авторизации.

Меняет ли ClientSession IP при каждом запросе?

Нет. Сессия может использовать уже открытое соединение. Ротация задаётся на шлюзе; отдельный вызов session.get() сам по себе не подтверждает новое соединение или другой выходной адрес.

Установите проверенную версию

Нужен Python 3.11 или новее в отдельном окружении проекта. Пример проверен с Python 3.12.14 и aiohttp 3.14.3. После установки убедитесь, что именно этот пакет импортирует интерпретатор вашего worker. Работающий notebook, shell и фоновый процесс могут пользоваться разными окружениями, даже если открыты из одной папки. Сохраните версию в зависимостях приложения.

Установка и проверка aiohttpsh
python -m pip install "aiohttp==3.14.3" certifi
python -c "import aiohttp; print(aiohttp.__version__)"

Возьмите полное имя пользователя из конструктора подключения и задайте переменную PROXY_USER в окружении процесса. Выбирайте пул и страну, доступные сейчас. Используйте пароль прокси, а не пароль сайта или ключ API. Скрипт запросит его без отображения символов. Для работы по расписанию заранее получите секрет через свой менеджер секретов, не записывая его в код.

Запустите короткую проверку HTTPS

aiohttp_check.py — запрос через шлюзpython
import asyncio
import getpass
import json
import os
import ssl
from urllib.parse import urlsplit

import aiohttp
import certifi


async def check(server, proxy_headers, tls):
    timeout = aiohttp.ClientTimeout(
        total=20, connect=5, sock_connect=5, sock_read=10
    )
    connector = aiohttp.TCPConnector(ssl=tls, limit=5, limit_per_host=5)
    try:
        async with aiohttp.ClientSession(
            connector=connector, timeout=timeout, trust_env=False,
            cookie_jar=aiohttp.DummyCookieJar(),
        ) as session:
            async with session.get(
                "https://api.portproof.org/v1/echo-ip", proxy=server, proxy_headers=proxy_headers,
                allow_redirects=False,
            ) as response:
                response.raise_for_status()
                if response.status != 200:
                    raise ValueError("Expected HTTP 200 without a redirect")
                body = bytearray()
                async for chunk in response.content.iter_chunked(8192):
                    if len(body) + len(chunk) > 65536:
                        raise ValueError("Echo response exceeds 64 KiB")
                    body.extend(chunk)
                payload = json.loads(body)
                if (not isinstance(payload, dict)
                        or not isinstance(payload.get("ip"), str)
                        or not payload["ip"]):
                    raise ValueError("Expected an IP address in the echo response")
                print("HTTP", response.status, "exit", payload["ip"])
    except aiohttp.ClientHttpProxyError as error:
        raise SystemExit(f"Proxy CONNECT refused: HTTP {error.status}") from None
    except aiohttp.ClientResponseError as error:
        raise SystemExit(f"HTTP request refused: {error.status}") from None
    except (aiohttp.ClientError, TimeoutError, ValueError) as error:
        raise SystemExit(f"Check failed: {type(error).__name__}") from None
    finally:
        # Let TLS transports finish closing before asyncio.run stops the loop.
        await asyncio.sleep(0.25)


if __name__ == "__main__":
    server = os.environ.get("PROXY_SERVER", "http://gw.portproof.org:7000")
    address = urlsplit(server)
    if (address.scheme != "http" or not address.hostname
            or address.port is None or address.username is not None
            or address.password is not None or address.path not in ("", "/")
            or address.query or address.fragment):
        raise SystemExit("Use an HTTP gateway URL without credentials or a path")
    proxy_headers = {
        "Proxy-Authorization": aiohttp.encode_basic_auth(
            os.environ["PROXY_USER"], getpass.getpass("Proxy password: ")
        )
    }
    tls = ssl.create_default_context(
        cafile=os.environ.get("PROXY_CA_BUNDLE") or certifi.where()
    )
    asyncio.run(check(server, proxy_headers, tls))

Сохраните файл и выполните python aiohttp_check.py. В уже работающем цикле событий подготовьте те же параметры и используйте await check(server, proxy_headers, tls). Вложенный asyncio.run() не нужен. Интерактивный запрос пароля выполняется до сетевой операции и не ограничивается её таймаутом. Скрипт печатает статус и выходной адрес, но не пароль и не полный текст исключения.

Ожидается HTTP 200 и JSON с непустым строковым полем ip. Перенаправления отключены: переход на другую страницу не должен скрывать результат проверки. Успешный ответ echo-сервиса относится только к этому запросу. Он не доказывает географию выхода, скорость пула или успех дальнейшей операции на другом сайте. Для приложения проверьте также ожидаемые поля собственного ответа.

Скрипт накапливает не больше 64 КиБ декодированного тела. Превышение завершает проверку с ошибкой. Это ограничение памяти примера, а не лимит оплачиваемого трафика: заголовки, сжатие, служебный обмен и буферизация относятся к другим измерениям. Для оценки рабочего задания сопоставляйте полезные результаты с изменением счётчика в кабинете, учитывая задержку его обновления.

Не отправляйте пароль прокси конечному сайту

Proxy-Authorization находится в proxy_headers отдельного вызова session.get(). Не переносите его в обычные headers сессии и не используйте auth сайта для входа на шлюз. В версии 3.14 интерфейс BasicAuth и параметр proxy_auth объявлены устаревшими. Пример использует encode_basic_auth; подробности доступны в документации aiohttp 3.14.3.

Имя и пароль кодируются как учётные данные, поэтому пунктуация в пароле не становится частью синтаксиса URL. Полученный заголовок остаётся секретом: Base64 можно декодировать. Отключите запись таких заголовков в диагностические логи. HTTPS к конечному сайту не шифрует начальную авторизацию CONNECT на HTTP-шлюзе; учитывайте это при выборе сети, из которой запускается worker.

trust_env=False оставляет маршрут явным: переменные окружения с другими прокси и NO_PROXY не выбирают другой путь для этой проверки. Доверие сертификатам задаётся certifi либо одобренным набором PROXY_CA_BUNDLE. Сохраняйте требования корпоративной сети, если они есть. Ошибка пароля не исправляется отключением проверки TLS, а ssl=False убирает отдельную проверку подлинности сайта.

Ограничьте ожидание и очередь задач

В примере отводится пять секунд на получение соединения и десять секунд между чтениями в пределах общего двадцатисекундного таймаута запроса. Это настройки диагностики, не обещание скорости сервиса. Если сервер присылает данные понемногу, таймаут чтения может не сработать; общий предел всё равно ограничивает запрос. Более длинные таймауты aiohttp округляет, поэтому фактическое время может отличаться от заданного.

Коннектор допускает до пяти одновременных соединений. Он не ограничивает количество задач, которые приложение уже создало и поставило в ожидание. Для большого разрешённого задания используйте ограниченную очередь, срок выполнения worker и понятную остановку при превышении бюджета. Значения полей описаны в справочнике ClientTimeout.

Переиспользуйте сессию в рамках связанного задания и закрывайте её после работы. Здесь DummyCookieJar не сохраняет cookie. Если ваш сценарий требует входа в аккаунт, настройте хранение cookie и разделение аккаунтов отдельно. После завершения есть короткая пауза для закрытия TLS-транспортов; она помогает штатному завершению, но не гарантирует мгновенное освобождение любого зависшего соединения.

Какие сценарии проверены

30 сентября 2026 года этот исполняемый пример прошёл локальные проверки через авторизованный CONNECT-прокси и HTTPS-сервер с тестовыми (самоподписанными) сертификатами. Проверялись пунктуация в пароле, отказ авторизации, недоверенный сертификат, размер ответа, неверный JSON, перенаправление, остановившаяся и медленная передача. Код в русской и английской версиях общий, поэтому примеры не расходятся при обновлении.

В успешном запросе конечный сервер не получил заголовок авторизации прокси. Перенаправления не выполнялись; сессия, ответ и коннектор закрывались. После завершения отдельного процесса открытого туннеля не осталось. Эти результаты относятся к указанной версии клиента и локальному стенду. Они не являются проверкой рабочего пула, геолокации, SOCKS5 или TLS-соединения с самим прокси.

Найдите этап, на котором возникла ошибка

Proxy CONNECT refused: HTTP 407
Шлюз отказал до создания туннеля. Проверьте полное имя, пароль прокси и состояние доступа. Продолжите по инструкции для 407.
ClientConnectorCertificateError
Проверьте имя конечного сайта, время системы и доверенную цепочку сертификатов. Оставьте проверку TLS включённой.
SocketTimeoutError или TimeoutError
Повторите небольшой echo-запрос, проверьте сеть worker и пределы ожидания. Не увеличивайте параллелизм во время диагностики.
HTTP request refused: 403 или 429
Определите источник ответа и соблюдайте его правила доступа или частоты запросов. Не повторяйте отказ бесконечно.

Здесь нет цикла повторов приложения, но библиотека может повторять некоторые подходящие запросы после сбоев соединения. Один вызов не гарантирует одну сетевую попытку. Учитывайте попытки при расчёте расхода и не переносите такую логику на операции с побочными эффектами. Ротация и sticky-сессии объясняют связь режима шлюза с переиспользованием соединений.

Если задача использует другой клиент, переходите к HTTPX или Requests. Для мониторинга цен можно оценить измеренный запуск в планировщике трафика — на английском. Перед покупкой сравните оба типа пулов и доступность нужного направления.

Допустимое использование

Выполняйте только разрешённое тестирование, мониторинг и исследования с соблюдением правил сайта и политики использования.

Следующий шаг

Прокси в aiohttp: авторизация и таймауты · Portproof