Python中asyncio批量获取时await对象为空,如何解决?
起因是我要用到协程去批量验证多个 http 地址情况:当前 url 地址 ,http 状态 ,title
import asyncio,aiohttp
from bs4 import BeautifulSoup
res=[]
async def fetch_async(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
url=resp.url
status=resp.status
text=await resp.read()
soup = BeautifulSoup(text, 'lxml')
title=soup.title.string
print(url,status,title)
#res.append(url,status,title)
tasks = [fetch_async(‘http://www.baidu.com/’), fetch_async(‘https://www.v2ex.com’)]
event_loop = asyncio.get_event_loop()
results = event_loop.run_until_complete(asyncio.gather(*tasks))
event_loop.close()
http://www.baidu.com/ 200 百度一下,你就知道
https://www.v2ex.com 200 V2EX
遵从一个函数只干一件事的原则,于是我将整个 response 对象返回到列表里了,一切 ok .
就当我要把它们取出的时候 。
for i in res: i.url —>https;//xx.com i.status —>200 i.read() ->null 由于 response 的 read() 要使用 await 不然是获取不到的。
我尝试 :
res.append(await response) print(len(res)) --> 0
现在是没有办法了,还望表哥们给点思路
Python中asyncio批量获取时await对象为空,如何解决?
asyncio.create_task(fetch_async('<a target="_blank" href="http://www.baidu.com/'" rel="nofollow noopener">http://www.baidu.com/'</a>))
2. 用 queue 而不是 list,并发执行是这里有竞争
这个问题我遇到过,通常是因为在异步循环中批量创建任务时,没有正确等待所有任务完成。
核心问题是:当你用asyncio.create_task()创建多个任务后,如果没有用asyncio.gather()或asyncio.wait()来收集结果,这些任务可能还在后台运行,你直接去访问结果就会得到空值。
看个具体例子:
import asyncio
async def fetch_data(item_id):
await asyncio.sleep(1) # 模拟网络请求
return f"data_{item_id}"
async def wrong_way():
tasks = []
for i in range(5):
task = asyncio.create_task(fetch_data(i))
tasks.append(task)
# 错误:直接访问task对象,此时任务可能还没完成
results = []
for task in tasks:
results.append(task) # 这里得到的是task对象,不是结果
return results
async def right_way():
tasks = []
for i in range(5):
task = asyncio.create_task(fetch_data(i))
tasks.append(task)
# 正确:等待所有任务完成并收集结果
results = await asyncio.gather(*tasks)
return results
# 测试错误方式
print("错误方式的结果:")
wrong_results = asyncio.run(wrong_way())
for r in wrong_results:
print(type(r), r) # 输出的是Task对象
print("\n正确方式的结果:")
right_results = asyncio.run(right_way())
for r in right_results:
print(r) # 输出实际的数据
关键点:
asyncio.create_task()只是把协程包装成Task并开始执行,不等待完成- 必须用
await asyncio.gather(*tasks)等待所有任务完成并获取结果 - 或者用
await asyncio.wait(tasks),然后从完成的task中提取结果
如果你用的是列表推导式创建任务,也要记得等待:
# 这样不行
tasks = [asyncio.create_task(fetch_data(i)) for i in range(5)]
# 任务还没完成就去访问
# 这样才行
tasks = [asyncio.create_task(fetch_data(i)) for i in range(5)]
results = await asyncio.gather(*tasks)
检查你的代码,确保在批量创建任务后正确等待了所有任务完成。
总结:用asyncio.gather等待所有任务完成再取结果。
遵从一个函数只干一件事的原则,于是我将整个 response 对象返回到列表里了
-----
你为何不将 response 读出来放到 res 里呢? 好好想想如何界定"一件事"
你必须在 async with session.get(url) as resp:这个上下文里面 await resp.read()或者 await resp.text() 你离开这个上下文这个链接已经关闭了,你就再也不能从这个 response 里面获取数据.
将整个 response 对象存起来,只能存到当时的状态,需要 await 的属性 就再也拿不到了嘛。
那我也有尝试过 append(await response ) 还是不可以
aiohttp 的 response 是惰性读取的,也就是只有你在 await resp.read()的时候才会真正的从 TCP 缓冲区读取,当你关闭这个 resp 的时候,http 连接已经关闭,就不可能读到数据了
当然你 await response 有什么用,这样既不会真正的读取,aiohttp 的 response 对象也不是可重用的,你只能自己 new 一个 dict 来把 resp 中你想要的数据保存出来,再 append 进 res
是的。response,只是带有一个从缓冲区取数据的方法,但你离开缓冲区了(不在 response 上下文),要去调用这个方法取数据也取不到数据了,你需要在没离开前取出数据 例如一个不是很好的实例 response.text = await response.text() 之后你就能从 response.text 拿到数据了

