Python 如何在循环中*正确*运行asyncio/aiohttp请求?

Python 如何在循环中*正确*运行asyncio/aiohttp请求?,python,asynchronous,python-requests,python-asyncio,aiohttp,Python,Asynchronous,Python Requests,Python Asyncio,Aiohttp,我试图同时请求一组URL,但是URL是从列表中构建的。目前,我正在循环列表,并(我认为)将它们添加到队列中。它肯定比requests.get快10倍,但是我不确定我做得是否正确,因此可以对其进行优化。我分析了它,注意到它在并发请求完成后90%的时间里仍然处于锁定状态,即开始->10+并发请求->锁定5秒左右->完成 此外,此代码会在结尾处生成一条未关闭的客户端会话消息。知道为什么吗?非常确定这是正确使用上下文管理器 我搜索了一下,没有找到这个确切的问题 import signal impor

我试图同时请求一组URL,但是URL是从列表中构建的。目前,我正在循环列表,并(我认为)将它们添加到队列中。它肯定比requests.get快10倍,但是我不确定我做得是否正确,因此可以对其进行优化。我分析了它,注意到它在并发请求完成后90%的时间里仍然处于锁定状态,即开始->10+并发请求->锁定5秒左右->完成

此外,此代码会在结尾处生成一条
未关闭的客户端会话
消息。知道为什么吗?非常确定这是正确使用上下文管理器

我搜索了一下,没有找到这个确切的问题

 import signal
 import sys
 import asyncio
 import aiohttp
 import json
 import requests

 lists = ['eth', 'btc', 'xmr', 'req', 'xlm', 'etc', 'omg', 'neo', 'btc', 'xmr', 'req', 'xlm', 'etc', 'omg', 'neo']

 loop = asyncio.get_event_loop()
 client = aiohttp.ClientSession(loop=loop)

 async def fetch(client, url):
     async with client.get(url) as resp:
         assert resp.status == 200
         return await resp.text()

 async def main(loop=loop, url=None):
     async with aiohttp.ClientSession(loop=loop) as client:
         html = await fetch(client, url)
         print(html)

 def signal_handler(signal, frame):
     loop.stop()
     client.close()
     sys.exit(0)

 signal.signal(signal.SIGINT, signal_handler)
 tasks = []
 for item in lists:
     url = "{url}/{endpoint}/{coin_name}".format(
                     url='https://coincap.io',
                     endpoint='page',
                     coin_name=item.upper()
                 )
     print(url)
     tasks.append(
         asyncio.ensure_future(main(url=url))
     )

 loop.run_until_complete(asyncio.gather(*tasks))

看起来你所做的很有效,但你认为你做的每件事都不完全正确:

  • 您创建了一个从未使用过的客户端,并且未正确关闭(导致
    未关闭的客户端会话
    )警告
  • 您正在为每个请求创建一个客户端,这比重用客户端效率低得多
  • 您没有在正在运行的事件循环中运行大部分代码
  • 如果您有长时间运行的异步IO任务,则不需要使用现有的信号处理程序
以下是我对您的代码的简化理解:

导入异步IO
进口aiohttp
列表=['eth','btc','xmr','req','xlm','etc','omg','neo','btc','xmr','req','xlm','etc','omg','neo']
异步def提取(客户端,项):
url='1〕https://coincap.io/{endpoint}/{coin_name}.格式(
endpoint='page',
coin_name=item.upper()
)
与客户端异步。获取(url)作为响应:
断言响应状态==200
html=wait resp.text()
打印(html)
异步def main():
与aiohttp.ClientSession()作为客户端异步:
等待asyncio.gather(*[
异步。确保未来(获取(客户端、项目))
对于列表中的项目
])
loop=asyncio.get\u event\u loop()
循环。运行\u直到完成(main())

如果您想随后处理html,您可以在fetch协同程序中执行,也可以对来自
聚集的所有结果进行操作

看起来像您所做的那样,但是您认为您没有完全正确地执行所有操作:

  • 您创建了一个从未使用过的客户端,并且未正确关闭(导致
    未关闭的客户端会话
    )警告
  • 您正在为每个请求创建一个客户端,这比重用客户端效率低得多
  • 您没有在正在运行的事件循环中运行大部分代码
  • 如果您有长时间运行的异步IO任务,则不需要使用现有的信号处理程序
以下是我对您的代码的简化理解:

导入异步IO
进口aiohttp
列表=['eth','btc','xmr','req','xlm','etc','omg','neo','btc','xmr','req','xlm','etc','omg','neo']
异步def提取(客户端,项):
url='1〕https://coincap.io/{endpoint}/{coin_name}.格式(
endpoint='page',
coin_name=item.upper()
)
与客户端异步。获取(url)作为响应:
断言响应状态==200
html=wait resp.text()
打印(html)
异步def main():
与aiohttp.ClientSession()作为客户端异步:
等待asyncio.gather(*[
异步。确保未来(获取(客户端、项目))
对于列表中的项目
])
loop=asyncio.get\u event\u loop()
循环。运行\u直到完成(main())

如果您想随后处理html,您可以在fetch Corroutine中执行,也可以对来自
gather

的所有结果进行操作。很好的答案,只有一个小问题:
create_task()
应该优先于
Sure_future()
使用,如果您知道您有一个Corroutine对象-。在这种情况下,这两种方法都不需要,因为
asyncio.gather
(和
asyncio.wait
等)将正确处理正在传递的协程对象,或者可以转换为
未来的任何其他对象。是的。我本想更改它,但忘记了,在这种情况下,
确保未来
将只调用
创建任务
,但最好使用
创建任务
。但这里您不需要它-
异步。聚集(*(获取列表中项目的(客户端,项目))
应该可以正常工作
collect
是接受协同路由或未来。很好的答案,只是一个小问题:
create\u task()
应该优先于
sure\u future()
使用,如果你知道你有一个协同路由对象-。在这种情况下,这两种方法都不需要,因为
asyncio.gather
(和
asyncio.wait
等)将正确处理正在传递的协程对象,或者可以转换为
未来的任何其他对象。是的。我本想更改它,但忘记了,在这种情况下,
确保未来
将只调用
创建任务
,但最好使用
创建任务
。但这里您不需要它-
异步。聚集(*(获取列表中项目的(客户端,项目))
应该可以正常工作<代码>收集
是接受合作计划或未来。