asyncio.gather с return_exceptions=True помещает исключения задач в свой список результатов. Поэтому ожидание пакета без исключения не означает, что каждый запрос через прокси завершился успешно.

Держите пункт назначения и выбранный прокси рядом с отправленными awaitable-объектами, затем проверяйте каждый результат:

outcomes = await asyncio.gather(*jobs, return_exceptions=True)

for (url, proxy), outcome in zip(routes, outcomes):
    if isinstance(outcome, BaseException):
        record_failure(url, proxy, outcome)
    else:
        record_success(url, proxy, outcome)

jobs содержит корутины запросов приложения; routes содержит соответствующие им пары (url, proxy) в том же порядке. Функции записи — это собственная обработка результатов приложения. gather сохраняет порядок входа, даже если порядок завершения отличается.

Возвращённый CancelledError дочерней задачи классифицируйте как отмену, а не обвиняйте её прокси. Отмена внешнего gather по-прежнему распространяется на незавершённые дочерние задачи; return_exceptions не превращает эту отмену в обычный успех.

Обработка HTTP-статуса также относится к операции запроса. aiohttp по умолчанию не возбуждает исключение при каждой ошибке HTTP, поэтому возвращайте статус или применяйте raise_for_status согласно политике задачи.

Как только результаты видны, приложение может отличать сбои соединения от ответов пункта назначения и решать, какая работа заслуживает ещё одной попытки.

Похожие статьи