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。

结果可见之后,应用就能区分连接失败与目标返回的响应,并决定哪些工作需要再试一次。

相关文章