python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python异步并发下载

Python使用asyncio+aiohttp实现高效异步并发下载

作者:weixin_46244623

这篇文章主要为大家详细介绍了如何使用 Python 的异步编程模型(asyncio)配合 aiohttp 库,实现真正高效的异步并发下载,大幅提升下载速度,感兴趣的小伙伴可以了解下

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

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

一、为什么选择 aiohttp + asyncio

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

二、安装依赖

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

pip install aiohttp

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

pip install aiofiles

三、核心代码实现

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

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 镜像),建议使用流式写入,避免一次性加载整个文件到内存:

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 控制并发数,避免打爆目标服务器或本地资源:

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 和超时

timeout = aiohttp.ClientTimeout(total=30)
async with aiohttp.ClientSession(
    timeout=timeout,
    headers={"User-Agent": "Mozilla/5.0 (Python aiohttp)"}
) as session:

3.自动从 URL 提取文件名

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"

六、总结

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

到此这篇关于Python使用asyncio+aiohttp实现高效异步并发下载的文章就介绍到这了,更多相关Python异步并发下载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文