asyncio.gather任务无法并发执行:网页并发爬取程序失效求助
嘿,我一眼就看出问题所在了——你这里的异步代码其实是「假异步」!
问题根源:同步阻塞IO破坏了异步并发
你虽然用了async/await的语法,但核心的网络请求requests.get(url)是同步阻塞的。当这个函数执行时,它会死死占用当前线程,直到服务器返回响应,asyncio的事件循环完全没机会切换到其他任务,所以看起来和同步代码没区别,根本没法实现并发。
另外,你手动创建asyncio.Future然后设置结果的操作完全是多余的——async函数本身返回的就是一个可等待对象,直接返回soup就行,不用多此一举封装Future。
修复方案:用异步HTTP库+正确的异步写法
我们需要把同步的requests换成异步的aiohttp,它是专门为asyncio设计的非阻塞HTTP客户端,能让事件循环在等待响应时去处理其他任务,真正实现并发。
以下是修正后的完整代码:
import asyncio from bs4 import BeautifulSoup import aiohttp async def return_soup(session, url): # 用aiohttp的异步请求代替requests的同步请求 async with session.get(url) as r: r.encoding = "utf-8" html = await r.text() soup = BeautifulSoup(html, "html.parser") return soup # 直接返回结果,无需手动创建Future async def parseURL_async(session, url): print(f"Started to download {url}") soup = await return_soup(session, url) print(f"Finished downloading {url}") return soup async def main(urls): # 创建一个全局的aiohttp Session,复用连接更高效 async with aiohttp.ClientSession() as session: # 创建多个异步任务,用gather并发执行 tasks = [parseURL_async(session, url) for url in urls] results = await asyncio.gather(*tasks) return results if __name__ == "__main__": # 测试用的URL列表 target_urls = [ "https://example.com", "https://example.org", "https://example.net" ] # 运行事件循环 results = asyncio.run(main(target_urls)) # 可以在这里处理爬取到的soup结果 print(f"共爬取了{len(results)}个页面")
关键改动点说明
- 替换requests为aiohttp:
aiohttp.ClientSession的get方法是异步的,配合await可以让事件循环在等待响应时切换到其他任务。 - 复用Session:创建一个全局的ClientSession可以复用TCP连接,提升爬取效率,这也是aiohttp的最佳实践。
- 用asyncio.gather管理并发任务:把多个
parseURL_async任务收集起来,让事件循环同时调度它们,真正实现并发。 - 去掉多余的Future封装:async函数直接返回结果即可,await会自动处理它的可等待对象特性。
现在运行这段代码,你会看到多个「Started to download」几乎同时打印,然后陆续出现「Finished downloading」,这就是真正的并发效果啦!
内容的提问来源于stack exchange,提问作者Blaszard
相关产品推荐
相关产品推荐

