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 避免内存溢出;
  • ✅ 生产环境中务必加入错误处理、超时控制、并发限制

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

相关推荐
书到用时方恨少!2 分钟前
Python Matplotlib 使用指南:数据可视化的画笔
python·信息可视化·matplotlib
92year9 分钟前
pip install agent-framework:微软多Agent框架1.0实测
python·ai·微软·agent·mcp
曾阿伦16 分钟前
Python 获取本机所有网卡 IP/MAC 地址
python·tcp/ip
qq_2837200519 分钟前
Python 操作 MySQL 数据库全解:增删改查、事务、连接池与性能优化
数据库·python·mysql
Leinwin20 分钟前
实战教程:3步接入Azure OpenAI调用GPT-5,国内IP直连
后端·python·flask
爱码小白21 分钟前
MySQL 系统函数专项练习题
数据库·python·mysql
傻啦嘿哟22 分钟前
Python 实现 Excel 数据可视化:柱状图制作教程
开发语言·python
ZC跨境爬虫24 分钟前
海南大学交友平台登录页开发实战day3(解决python传输并读取登录信息的问题)
前端·数据库·python·html
2601_9544345526 分钟前
2026年电钢琴品牌专业深度测评:排名前五权威榜单发布
大数据·人工智能·python
威联通网络存储29 分钟前
非结构化数据治理:底层全文检索与自动化归档解析
运维·python·自动化·全文检索