杰瑞科技汇

Python requests模块如何实现文件下载?

requests 是 Python 中最流行、最易用的 HTTP 库,它极大地简化了 HTTP 请求的发送,与 Python 内置的 urllib 相比,requests 的 API 更加简洁和人性化。

Python requests模块如何实现文件下载?-图1
(图片来源网络,侵删)

第一步:安装 requests 模块

如果你的 Python 环境中还没有安装 requests,首先需要安装它,打开你的终端或命令行工具,运行以下命令:

pip install requests

第二步:基本下载方法

下载文件的核心思想是:发送一个 GET 请求到文件的 URL,然后将获取到的二进制内容写入到一个本地文件中。

示例:下载一张图片

假设我们要下载这张示例图片:https://www.python.org/static/community_logos/python-logo-master-v3-TM.png

import requests
import os
# 1. 定义要下载的文件URL和本地保存路径
url = "https://www.python.org/static/community_logos/python-logo-master-v3-TM.png"
local_filename = "python_logo.png" # 你想保存的本地文件名
# 2. 发送GET请求
# stream=True 参数非常重要,它不会立即下载整个文件内容到内存,
# 而是允许你以流的方式逐步获取内容,这对于大文件下载非常关键。
try:
    with requests.get(url, stream=True) as r:
        # 检查请求是否成功 (状态码 200)
        r.raise_for_status() 
        # 3. 获取文件总大小(可选,用于显示进度)
        total_size = int(r.headers.get('content-length', 0))
        # 4. 打开本地文件以二进制写入模式 ('wb')
        with open(local_filename, 'wb') as f:
            # 5. 分块写入文件
            # iter_content(chunk_size=8192) 会将内容分块迭代,8192字节是一个常见的块大小
            for chunk in r.iter_content(chunk_size=8192):
                # 过滤掉保持连接产生的新块
                if chunk:
                    f.write(chunk)
        print(f"文件已成功下载并保存为 {local_filename}")
except requests.exceptions.RequestException as e:
    print(f"下载失败: {e}")

代码解析:

  1. requests.get(url, stream=True):

    Python requests模块如何实现文件下载?-图2
    (图片来源网络,侵删)
    • requests.get() 用于发送一个 GET 请求。
    • stream=True 是关键,它告诉 requests 不要立即下载响应体(即文件内容),而是先获取响应头,这可以节省大量内存,特别是下载大文件时,当 stream=True 时,r.content 不可用,必须使用 iter_content()
  2. r.raise_for_status():

    • 这是一个非常实用的方法,如果请求返回的状态码表示错误(404 Not Found 或 500 Server Error),它会抛出 requests.exceptions.HTTPError 异常,这有助于我们快速发现下载失败的原因。
  3. with open(...):

    • 使用 with 语句可以确保文件在操作完成后被正确关闭,即使发生错误也是如此。
    • 'wb' 是文件打开模式,w 表示写入,b 表示二进制模式。下载文件(尤其是图片、视频、压缩包等非文本文件)时,必须使用二进制模式。
  4. r.iter_content(chunk_size=8192):

    • stream=True 时,这是获取响应内容的标准方式。
    • 它会返回一个迭代器,每次迭代返回 chunk_size 大小的数据块。
    • 我们将这些数据块逐个写入文件,直到下载完成。

第三步:更高级的功能与最佳实践

显示下载进度条

下载大文件时,显示一个进度条会非常有用,我们可以结合 tqdm 库来实现。

Python requests模块如何实现文件下载?-图3
(图片来源网络,侵删)

首先安装 tqdm:

pip install tqdm

然后修改代码:

import requests
from tqdm import tqdm
import os
url = "https://www.python.org/static/community_logos/python-logo-master-v3-TM.png"
local_filename = "python_logo_with_progress.png"
try:
    # 发送请求,流式传输
    with requests.get(url, stream=True) as r:
        r.raise_for_status()
        # 获取文件总大小
        total_size = int(r.headers.get('content-length', 0))
        # 初始化tqdm进度条
        # total=total_size 设置总大小
        # unit='B' 单位是字节
        # unit_scale=True 自动缩放单位 (KB, MB, GB)
        # desc=filename 设置进度条描述
        progress_bar = tqdm(total=total_size, unit='B', unit_scale=True, desc=local_filename)
        # 写入文件
        with open(local_filename, 'wb') as f:
            for chunk in r.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)
                    # 更新进度条,每次写入的字节数
                    progress_bar.update(len(chunk))
        # 关闭进度条
        progress_bar.close()
        print(f"\n文件已成功下载并保存为 {local_filename}")
except requests.exceptions.RequestException as e:
    print(f"下载失败: {e}")

处理重定向

requests 默认会处理 URL 重定向,你无需关心最终下载的文件来自哪个链接,如果你想查看最终的 URL,可以使用 r.url

import requests
url = "http://github.com/favicon.ico" # 这个URL会重定向
r = requests.get(url, stream=True)
print(f"请求的原始 URL: {url}")
print(f"最终下载的 URL: {r.url}") # 会显示最终的重定向地址

处理超大文件(流式下载到磁盘)

对于非常大的文件(如几十GB的ISO镜像),一次性将其读入内存是绝对不可行的。requests 的流式下载机制天生就是为了解决这个问题,上面的基本示例已经完美地实现了这一点,它不会将整个文件加载到内存中。

处理需要登录或认证的文件

如果下载的链接需要登录或者有访问权限限制(如 Bearer Token),你可以在请求中添加相应的头部信息。

import requests
# 假设这是一个需要认证的URL
url = "https://api.example.com/protected_file.zip"
# 假设你的token或认证信息
headers = {
    "Authorization": "Bearer YOUR_API_TOKEN_HERE"
}
local_filename = "protected_file.zip"
try:
    with requests.get(url, headers=headers, stream=True) as r:
        r.raise_for_status()
        with open(local_filename, 'wb') as f:
            for chunk in r.iter_content(chunk_size=8192):
                f.write(chunk(chunk)
        print(f"文件 {local_filename} 下载成功。")
except requests.exceptions.RequestException as e:
    print(f"下载失败: {e}")

优雅地处理网络中断和断点续传

上面的代码如果在中途断网,再次运行会从头开始下载,要实现断点续传,需要用到 HTTP 的 Range 请求头。

import os
import requests
url = "https://www.python.org/static/community_logos/python-logo-master-v3-TM.png"
local_filename = "python_logo_resume.png"
# 检查本地是否已有部分文件
if os.path.exists(local_filename):
    # 获取已下载文件的大小
    downloaded_size = os.path.getsize(local_filename)
    # 设置Range请求头,从已下载大小的位置继续下载
    headers = {'Range': f'bytes={downloaded_size}-'}
else:
    downloaded_size = 0
    headers = {}
try:
    with requests.get(url, headers=headers, stream=True) as r:
        # 如果服务器支持断点续传,状态码会是 206 Partial Content
        if r.status_code == 206:
            print("正在断点续传...")
            # 以追加模式 ('ab') 打开文件
            mode = 'ab'
        elif r.status_code == 200:
            print("开始新下载...")
            # 如果没有已下载的文件,从头开始下载
            mode = 'wb'
        else:
            r.raise_for_status() # 其他错误则抛出异常
        with open(local_filename, mode) as f:
            for chunk in r.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)
        print(f"文件下载完成,总大小: {os.path.getsize(local_filename)} bytes")
except requests.exceptions.RequestException as e:
    print(f"下载失败: {e}")

功能 方法/要点 说明
基本下载 requests.get(url, stream=True) 启用流式下载,避免内存爆炸。
写入文件 open(filename, 'wb') 必须使用二进制写入模式
for chunk in r.iter_content(chunk_size=...): 分块读取和写入,处理大文件的核心。
错误处理 r.raise_for_status() 简洁地检查HTTP错误。
进度显示 tqdm iter_content 循环添加进度条,提升用户体验。
认证下载 requests.get(url, headers={'Authorization': '...'}) 在请求头中添加认证信息。
断点续传 Range 请求头 + 'ab' 追加模式 实现下载中断后恢复,需要服务器支持。

requests 模块功能强大,以上内容涵盖了从入门到进阶的文件下载场景,希望对你有帮助!

分享:
扫描分享到社交APP
上一篇
下一篇