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

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

相关推荐
2401_8314194410 小时前
Layui如何实现表格内部的图片点击后进入相册轮播模式
jvm·数据库·python
Jetev10 小时前
SQL使用JOIN连接更新目标表数据_UPDATE语句结合JOIN语法实现
jvm·数据库·python
2401_8323655210 小时前
如何用最快速度完成Navicat的Linux系统完美安装配置_傻瓜式操作步骤
jvm·数据库·python
财经资讯数据_灵砚智能10 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年4月29日
大数据·人工智能·python·信息可视化·自然语言处理
SilentSamsara10 小时前
Python 内存管理:引用计数、循环垃圾回收与内存泄漏排查
开发语言·vscode·python·青少年编程·pycharm
AI技术增长10 小时前
Pytorch图像去噪实战(七):Noise2Noise自监督图像去噪实战,没有干净图也能训练模型
人工智能·pytorch·python
PSLoverS10 小时前
Navicat全局查找与替换字符突然失效怎么办_重置与缓存清理
jvm·数据库·python
m0_6028577611 小时前
如何提升SQL存储过程逻辑复用_封装通用存储过程函数
jvm·数据库·python
傻啦嘿哟12 小时前
如何在 Python 中使用 colorama 库来给输出添加颜色
开发语言·python
forEverPlume12 小时前
mysql如何实现高可用集群架构_基于MHA环境搭建与部署
jvm·数据库·python