1. ホーム
  2. python

実行中のイベントループから asyncio.run() を呼び出すことができない

2023-09-10 23:22:10

質問

asyncioを使ってWebページのhtmlを取得したいです。

jupyter notebookで以下のコードを実行しました。

import aiofiles
import aiohttp
from aiohttp import ClientSession

async def get_info(url, session):
    resp = await session.request(method="GET", url=url)
    resp.raise_for_status()
    html = await resp.text(encoding='GB18030')
    with open('test_asyncio.html', 'w', encoding='utf-8-sig') as f:
        f.write(html)
    return html
    
async def main(urls):
    async with ClientSession() as session:
        tasks = [get_info(url, session) for url in urls]
        return await asyncio.gather(*tasks)

if __name__ == "__main__":
    url = ['http://huanyuntianxiazh.fang.com/house/1010123799/housedetail.htm', 'http://zhaoshangyonghefu010.fang.com/house/1010126863/housedetail.htm']
    result = asyncio.run(main(url))

しかし、これは RuntimeError: asyncio.run() cannot be called from a running event loop

何が問題なのでしょうか?

どうすれば解決できるのか?

どのように解決するのですか?

この asyncio.run() のドキュメントにはこうあります。

この関数 は、同じスレッドで別のasyncioイベントループが実行されているときは呼び出されません。

あなたの場合、jupyter ( IPython ≥ 7.0 ) はすでにイベントループを実行しています。

これでIPythonのターミナルやノートブックのトップレベルでasync/awaitが使えるようになり、ほとんどの場合 - "just work "になるはずです。IPython をバージョン 7+ に、IPykernel をバージョン 5+ に更新すれば、レースへの出発です。

したがって、イベントループを自分で開始する必要はなく、代わりに await main(url) を直接呼び出すことができます。

Jupyter / IPython

async def main():
    print(1)
    
await main()

Python (≥ 3.7)

import asyncio

async def main():
    print(1)
    
asyncio.run(main())

あなたのコードでは、それは与えるでしょう。

url = ['url1', 'url2']
result = await main(url)

for text in result:
    pass # text contains your html (text) response

注意事項

には 若干の違い はIPythonと比較して、Jupyterがどのようにループを使用するかについて、違いがあります。