Python asyncio, aiohttpでエラー時にリトライする

- Python -
2023.08.14
Python[パイソン]

while response is None: ...のようにwhile文でリトライする。

詳細は本文にて。

Pythonで何千回もHTTPリクエストを投げまくらないといけなくて、requestsで単純なループをするだけだと何十分もかかってしまうので、asyncio + aiohttpで非同期処理をすることに。

ChatGPTの助けを得つつ処理自体は通るコードができたけど、問題は以下14行目response = await fetch_url(session, url)でURLアクセス時にタイムアウト等の例外が発生したときにどうやってリトライするのか?ということでした。

(以下、省略のため記載していない関数あり)

Pythonimport aiohttp
import asyncio


async def fetch_url(session, url):
    async with session.get(url) as response:
        return await response.content.read()


async def process_sheet(sheet):
    async def process_url(i, session, url):
        try:
            # ↓こいつをリトライかけたい
            response = await fetch_url(session, url))
        except Exception as e:
            print(f'エラー行 {i+1}:URL{url}: {e}')
        print(f'{i+1}行目 処理中.....')
        # ・・・以後の処理は省略

    url_list = get_urls(sheet)
    async with aiohttp.ClientSession() as session:
        tasks = [process_url(i, session, url) for i, url in enumerate(url_list)]
        await asyncio.gather(*tasks)


def main():
    sheet = get_sheet()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(process_sheet(sheet))
    loop.close()


if __name__ == "__main__":
    main()

実際、fetch_urlでいくつものURLに対して例外が発生してしまい「タイムアウトならもう一度リクエストを投げれば大丈夫なのでは?」と思うもリトライのやり方が分からず。

1020行目 処理中.....
1024行目 処理中.....
エラー行 1023:URL:https://xxxxxxx:
エラー行 1021:URL:https://xxxxxxx:
エラー行 1082:URL:https://xxxxxxx:
エラー行 1068:URL:https://xxxxxxx:
エラー行 1046:URL:https://xxxxxxx:
・・・↑これにリトライをかけたい・・・

async/awaitの文法を勉強し始めたばかりで、async、awaitをつけたからといってどのように裏で動いているのか理解できず、かつコードの骨子はChatGPTに生成してもらったため、どういじっていいのか分からず困り果てていました。

Python asyncio, aiohttpで例外発生時にwhile文でリトライする

以下のstack overflowの回答を参考に、while文を使ったら上手くできました。

参考Fetch multiple URLs with asyncio/aiohttp and retry for failures

Pythonasync def process_sheet(sheet):
    async def process_url(i, session, url):
        max_retries = 3
        retries = 0
        response = None
        while response is None and retries < max_retries:
            try:
                response = await fetch_url(session, url)
            except Exception as e:
                retries += 1
                print(f'エラー行 {i+1}:URL{url}: {e}')
            print(f'{i+1}行目 処理中')
            # ・・・以後の処理は省略

response = Noneのあいだは3回までリトライします。

※ 上記、簡略化のためexcept Exceptionと例外をひとまとめにしている点はご注意

これで、以下のようにエラーとなったURLに対しても再度fetch_urlできるようになりました。

2803行目 処理中.....
2804行目 処理中.....
2805行目 処理中.....
2806行目 処理中.....
エラー行 2889:URL:https://xxxxxxx:
エラー行 2831:URL:https://xxxxxxx:
エラー行 2834:URL:https://xxxxxxx:
エラー行 2857:URL:https://xxxxxxx:
↓リトライ処理が走っている↓
2889行目 処理中.....
2831行目 処理中.....
2834行目 処理中.....
2857行目 処理中.....

↑TOP