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

第一步:安装 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}")
代码解析:
-
requests.get(url, stream=True):
(图片来源网络,侵删)requests.get()用于发送一个 GET 请求。stream=True是关键,它告诉requests不要立即下载响应体(即文件内容),而是先获取响应头,这可以节省大量内存,特别是下载大文件时,当stream=True时,r.content不可用,必须使用iter_content()。
-
r.raise_for_status():- 这是一个非常实用的方法,如果请求返回的状态码表示错误(404 Not Found 或 500 Server Error),它会抛出
requests.exceptions.HTTPError异常,这有助于我们快速发现下载失败的原因。
- 这是一个非常实用的方法,如果请求返回的状态码表示错误(404 Not Found 或 500 Server Error),它会抛出
-
with open(...):- 使用
with语句可以确保文件在操作完成后被正确关闭,即使发生错误也是如此。 'wb'是文件打开模式,w表示写入,b表示二进制模式。下载文件(尤其是图片、视频、压缩包等非文本文件)时,必须使用二进制模式。
- 使用
-
r.iter_content(chunk_size=8192):- 当
stream=True时,这是获取响应内容的标准方式。 - 它会返回一个迭代器,每次迭代返回
chunk_size大小的数据块。 - 我们将这些数据块逐个写入文件,直到下载完成。
- 当
第三步:更高级的功能与最佳实践
显示下载进度条
下载大文件时,显示一个进度条会非常有用,我们可以结合 tqdm 库来实现。

首先安装 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 模块功能强大,以上内容涵盖了从入门到进阶的文件下载场景,希望对你有帮助!
