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

asyncio异步爬虫超时与并发数控制功能异常问题求解

问题根源

你的代码出现该问题的核心逻辑缺陷有两个:

  1. 仅在active_calls归0时才执行event.set()唤醒等待的任务,导致所有任务必须分批执行,每批要等上一批全部跑完才会启动下一批。
  2. 任务被唤醒后没有再次校验当前并发数,所有等待的任务会同时进入执行逻辑,自然就突破了5个并发的限制。
    另外你手动维护全局计数器+Event的实现方式很容易出现竞态条件,多个任务同时读写active_calls时可能出现计数不准的问题。

修复方案

asyncio提供了原生的并发限制工具asyncio.Semaphore,天生就是用来控制异步任务并发数的,不需要你手动维护计数器和Event,逻辑更简洁也更稳定。

修改后的完整代码

import json
from bs4 import BeautifulSoup
import asyncio 
import aiohttp 

# 配置项
MAX_CALLS = 5
DELAY = 0.3
PATH = "./你的存储路径/"  # 请补充你自己的文件存储路径

def write_to_txt_file(text, name):    
    # 用上下文管理器避免文件句柄泄露
    with open(f'{PATH}{name}.txt', 'w', encoding='utf-8') as f:
        f.write(text)

async def getData(item, session, request_delay, semaphore):
    # 先获取Semaphore许可,最多同时5个任务获取到
    async with semaphore:
        print('start', item['ID'])
        # 执行请求前延迟
        await asyncio.sleep(request_delay)
        try:
            async with session.get(url=item['Link']) as response:
                soup = BeautifulSoup(await response.text(), 'html.parser')
                name = f"{item['ID']}. {item['Title'][:100]}"
                text = soup.find(id="begin").get_text()
                write_to_txt_file(text, name)
        except Exception as e:
            # 建议加上异常捕获,避免单个请求出错导致整个脚本崩溃
            print(f"请求{item['Link']}出错:{str(e)}")

async def parseFromJson(): 
    with open('./data2/csvjson.json', encoding='utf-8') as data_file:
        data = json.load(data_file)
    # 初始化Semaphore,指定最大并发数
    semaphore = asyncio.Semaphore(MAX_CALLS)
    async with aiohttp.ClientSession() as session: 
        tasks = [] 
        next_delay = 0.1
        for item in data:
            # 每个任务的延迟在创建时就计算好,避免全局变量冲突
            task = asyncio.create_task(getData(item, session, next_delay, semaphore))
            next_delay += DELAY
            tasks.append(task) 
        await asyncio.gather(*tasks)

def main():
    asyncio.run(parseFromJson())
    
if __name__ == "__main__":
    main()

核心修改点说明

  • 去掉了手动维护的active_calls计数器和asyncio.Event,换成asyncio.Semaphore来自动控制并发数,每个任务执行前先获取许可,执行完自动释放,天然保证最多5个并发。
  • 修复了延迟参数的传参问题,每个任务的延迟在创建时就计算好传入,避免全局变量读写冲突。
  • 优化了文件写入逻辑,用上下文管理器避免文件句柄泄露,增加了请求异常捕获,避免单个请求失败导致整个脚本崩溃。
  • 补充了编码声明,避免读写中文时出现乱码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 15:15:03