Python 异步下载文件实战:使用 asyncio + aiohttp 实现高并发下载

在处理多个远程文件下载任务时,如果采用传统的同步方式(如 requests.get() 逐个下载),程序会因等待网络 I/O 而长时间阻塞,效率极低。

本文将教你如何使用 Python 的异步编程模型(asyncio)配合 aiohttp ,实现真正高效的异步并发下载,大幅提升下载速度,尤其适用于批量下载图片、视频、压缩包等场景。


一、为什么选择 aiohttp + asyncio?

  • aiohttp 是基于 asyncio 的高性能异步 HTTP 客户端/服务器框架;
  • ✅ 支持异步请求、自动连接池、重定向、超时控制;
  • ✅ 语法简洁,性能远超多线程或同步方案;
  • ✅ 适合 I/O 密集型任务(如网络下载)。

💡 注意:Python 的"异步" ≠ "多线程",它是通过事件循环(Event Loop)在单线程中并发处理多个 I/O 操作,避免阻塞。


二、安装依赖

首先安装 aiohttp(若未安装):

bash 复制代码
pip install aiohttp

推荐同时安装 aiofiles(用于异步写入文件):

bash 复制代码
pip install aiofiles

三、核心代码实现

方法一:将内容全部加载到内存后保存(适合中小文件)

python 复制代码
import asyncio
import aiohttp
import os

async def download_file(session, url, filename):
    """异步下载单个文件"""
    try:
        async with session.get(url) as response:
            if response.status == 200:
                content = await response.read()
                with open(filename, 'wb') as f:
                    f.write(content)
                print(f"✅ 下载成功: {filename}")
            else:
                print(f"❌ 下载失败 ({response.status}): {url}")
    except Exception as e:
        print(f"⚠️  下载出错: {url} | 错误: {e}")

async def download_all(urls, folder="downloads"):
    """并发下载多个文件"""
    os.makedirs(folder, exist_ok=True)

    # 创建 aiohttp 客户端会话(支持连接复用)
    async with aiohttp.ClientSession() as session:
        tasks = []
        for i, url in enumerate(urls):
            # 生成文件名(可根据需要自定义)
            ext = os.path.splitext(url.split('?')[0])[-1] or '.bin'
            filename = os.path.join(folder, f"file_{i+1}{ext}")
            task = asyncio.create_task(download_file(session, url, filename))
            tasks.append(task)

        # 并发执行所有下载任务
        await asyncio.gather(*tasks)

# 使用示例
if __name__ == "__main__":
    urls = [
        "https://example.com/file1.zip",
        "https://example.com/file2.jpg",
        "https://example.com/file3.pdf",
    ]

    print("🚀 开始异步下载...")
    asyncio.run(download_all(urls))
    print("🎉 所有文件下载完成!")

方法二:流式下载(适合大文件,节省内存)

对于大文件(如视频、ISO 镜像),建议使用流式写入,避免一次性加载整个文件到内存:

python 复制代码
import aiofiles  # 需要额外安装

async def download_file_stream(session, url, filename):
    try:
        async with session.get(url) as response:
            if response.status == 200:
                async with aiofiles.open(filename, 'wb') as f:
                    async for chunk in response.content.iter_chunked(8192):
                        await f.write(chunk)
                print(f"✅ 流式下载成功: {filename}")
            else:
                print(f"❌ 下载失败 ({response.status}): {url}")
    except Exception as e:
        print(f"⚠️  流式下载出错: {url} | 错误: {e}")

# 在 download_all 中替换 download_file 为 download_file_stream 即可

四、性能优势对比

方式 3 个 50MB 文件耗时(估算)
同步下载(requests) ~45 秒
多线程(ThreadPoolExecutor) ~20 秒
异步下载(aiohttp + asyncio) ~15--18 秒

📌 实际提升取决于网络带宽和服务器并发能力。异步方案在高延迟或大量小文件场景下优势更明显。


五、进阶优化建议

  1. 限制并发数量

    若 URL 数量极大(如 1000+),可使用 asyncio.Semaphore 控制并发数,避免打爆目标服务器或本地资源:

    python 复制代码
    semaphore = asyncio.Semaphore(10)  # 最多 10 个并发
    
    async def download_file_limited(session, url, filename):
        async with semaphore:
            await download_file(session, url, filename)
  2. 添加 User-Agent 和超时

    python 复制代码
    timeout = aiohttp.ClientTimeout(total=30)
    async with aiohttp.ClientSession(
        timeout=timeout,
        headers={"User-Agent": "Mozilla/5.0 (Python aiohttp)"}
    ) as session:
  3. 自动从 URL 提取文件名

    python 复制代码
    from urllib.parse import urlparse
    import os
    
    def get_filename_from_url(url):
        path = urlparse(url).path
        name = os.path.basename(path)
        return name if name else "downloaded_file"

六、总结

  • ✅ 使用 asyncio + aiohttp 是 Python 实现高并发下载的最佳实践
  • ✅ 异步下载能显著提升效率,尤其适合批量、I/O 密集型任务;
  • ✅ 对于大文件,推荐使用流式下载 + aiofiles 避免内存溢出;
  • ✅ 生产环境中务必加入错误处理、超时控制、并发限制

掌握这套异步下载方案,你就能轻松应对各种批量资源采集、自动化备份、数据同步等需求!

相关推荐
亓才孓18 分钟前
[Class类的应用]反射的理解
开发语言·python
小镇敲码人27 分钟前
深入剖析华为CANN框架下的Ops-CV仓库:从入门到实战指南
c++·python·华为·cann
摘星编程1 小时前
深入理解CANN ops-nn BatchNormalization算子:训练加速的关键技术
python
魔芋红茶1 小时前
Python 项目版本控制
开发语言·python
lili-felicity1 小时前
CANN批处理优化技巧:从动态批处理到流水线并行
人工智能·python
一个有梦有戏的人1 小时前
Python3基础:进阶基础,筑牢编程底层能力
后端·python
摘星编程1 小时前
解析CANN ops-nn中的Transpose算子:张量维度变换的高效实现
python
Liekkas Kono1 小时前
RapidOCR Python 贡献指南
开发语言·python·rapidocr
玄同7652 小时前
Python 后端三剑客:FastAPI/Flask/Django 对比与 LLM 开发选型指南
人工智能·python·机器学习·自然语言处理·django·flask·fastapi
爱吃泡芙的小白白2 小时前
环境数据多维关系探索利器:Pairs Plot 完全指南
python·信息可视化·数据分析·环境领域·pairs plot