使用 Jupyter Notebook 时“无法从正在运行的事件循环中调用 asyncio.run()”

新手上路,请多包涵

我想使用 asyncio 获取网页 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

问题是什么?

如何解决?

原文由 Chan 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.2k
2 个回答

asyncio.run() 文档说:

当另一个异步事件循环在同一线程中运行时, 无法 调用此函数。

在您的情况下,jupyter( IPython ≥ 7.0 )已经在运行事件循环:

您现在可以在 IPython 终端和笔记本的顶层使用 async/await,在大多数情况下,它应该“正常工作”。将 IPython 更新到版本 7+,将 IPykernel 更新到版本 5+,然后您就可以开始比赛了。

因此,您不需要自己启动事件循环,而是可以直接调用 await main(url) ,即使您的代码位于任何异步函数之外。

木星 / IPython

 async def main():
    print(1)

await main()

Python (≥ 3.7) 或旧版本的 IPython

 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 使用循环的方式 略有不同

原文由 cglacet 发布,翻译遵循 CC BY-SA 4.0 许可协议

添加到 cglacet 的答案 - 如果要检测循环是否正在运行并自动调整(即运行 main() 在现有循环上,否则 asyncio.run() ), 这是一个可能有用的片段:

 # async def main():
#     ...

try:
    loop = asyncio.get_running_loop()
except RuntimeError:  # 'RuntimeError: There is no current event loop...'
    loop = None

if loop and loop.is_running():
    print('Async event loop already running. Adding coroutine to the event loop.')
    tsk = loop.create_task(main())
    # ^-- https://docs.python.org/3/library/asyncio-task.html#task-object
    # Optionally, a callback function can be executed when the coroutine completes
    tsk.add_done_callback(
        lambda t: print(f'Task done with result={t.result()}  << return val of main()'))
else:
    print('Starting new event loop')
    result = asyncio.run(main())

原文由 Jean Monet 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题