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

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

相关推荐
bloglin999994 小时前
anaconda环境中如何生成requirements
python
【赫兹威客】浩哥4 小时前
【赫兹威客】框架模板-后端bat脚本部署教程
python·django
Cestb0n4 小时前
某果app 加密校验算法逆向分析
python·算法·逆向安全
薛定谔的猫喵喵4 小时前
解决 xlrd 2.0+ 版本只支持 xls 格式的问题
python·excel
2501_941805314 小时前
使用Python和Go构建高性能分布式任务调度系统的实践分享
分布式·python·golang
OLOLOadsd1234 小时前
基于YOLO11-C3k2-LFE的岩石颗粒智能检测与识别系统_2
python
_pass_4 小时前
Agent-Chat模式核心流程解析
python
徐114 小时前
自动化检测系统投资回报分析:思看科技如何助力企业降本增效
python·科技·物联网·自动化
q_35488851535 小时前
机器学习:Python地铁人流量数据分析与预测系统 基于python地铁数据分析系统+可视化 时间序列预测算法 ✅
大数据·人工智能·python·算法·机器学习·信息可视化·数据分析