You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么我的异步GET请求比同步慢?如何优化100次页面抓取效率?

问题核心错误点

  • 串行执行异步任务:当前get_n_soups_async的写法是每发起一个请求就用await等它完全结束,再发起下一个请求,本质和同步串行逻辑完全一致,没有利用到异步I/O的并发特性,反而多出了异步框架的调度开销,所以耗时比同步版本更长。
  • 等待逻辑冗余:你在每次请求完成后固定等待0.5秒,99次等待就有接近50秒的无效耗时,完全没有必要通过单请求间隔的方式控频。
  • 未使用批量异步调度:异步的核心优势是在等待某一个请求的响应时,切换去处理其他已经发起的请求,你的写法完全没有用到这个特性。
  • 注意隐形坑:如果你底层的get_response_text是封装的同步requests方法,哪怕上层加了async/await也完全不会有异步效果,必须使用异步HTTP客户端实现。

优化方案

你需要用asyncio.gather批量发起异步请求,同时通过信号量控制并发数上限,即可在不触发IMDb频率限制的前提下大幅提升效率,修改后的参考代码如下:

import asyncio
import aiohttp
import time
import requests
from bs4 import BeautifulSoup

# 异步请求文本方法,基于aiohttp实现
async def get_response_text(session, url):
    async with session.get(url) as resp:
        return await resp.text()

async def get_page_soup(session, url):
    response_text = await get_response_text(session, url)
    return BeautifulSoup(response_text, features="html.parser")

async def get_n_soups_async(url, num_soups=100, max_concurrent=8):
    # 信号量控制最大并发数,根据IMDb限频规则调整,一般5-10不会触发限制
    semaphore = asyncio.Semaphore(max_concurrent)
    async with aiohttp.ClientSession() as session:
        async def bounded_get_soup():
            async with semaphore:
                return await get_page_soup(session, url)
        # 批量生成100个任务同步调度
        tasks = [bounded_get_soup() for _ in range(num_soups)]
        soups = await asyncio.gather(*tasks)
    return soups

# 原同步版本代码
def get_n_soups_sync(url, num_soups=100):
    soup = None
    for _ in range(num_soups):
        soup = BeautifulSoup(requests.get(url).text, features="html.parser")
        time.sleep(0.5)
    return soup

async def main():
    print("Async main() has started... ")

    t1 = time.perf_counter()
    soups = await get_n_soups_async('https://www.imdb.com/name/nm0425005', 100)
    t2 = time.perf_counter()
    print(f"异步耗时:{t2 - t1:.2f}秒,返回soup数量:{len(soups)}")

    # 同步测试可按需开启,避免重复请求浪费时间
    # t1 = time.perf_counter()
    # soup = get_n_soups_sync('https://www.imdb.com/name/nm0425005', 100)
    # t2 = time.perf_counter()
    # print(f"同步耗时:{t2 - t1:.2f}秒")

    print("Async main() is over.")

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

优化效果说明

调整max_concurrent到合理范围后,总耗时可降到10-30秒区间,远快于原同步版本的230秒。另外原代码只返回了最后一个soup对象,优化后会返回所有100个soup的列表,符合你最初生成100个BeautifulSoup对象的需求。

内容的提问来源于stack exchange,提问作者alekscooper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.09 10:39:00