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 を適用します。

結果が見えるようになれば、アプリケーションは接続の失敗と宛先からの応答を区別し、どの作業を再試行する価値があるか判断できます。

関連記事