Python如何实现文件下载时的自动重试与错误捕获机制?

秋瑶吖_4264

秋瑶吖_4264

2026-07-03

732人浏览

原创

应使用tenacity库实现可控重试,针对connectionerror和timeout异常,配合状态码分类处理(4xx不重试、5xx重试)、range续传及线程池并发控制。

python如何实现文件下载时的自动重试与错误捕获机制?

requests.get() 失败时怎么加重试逻辑?

直接调用 requests.get() 遇到网络抖动或服务端临时不可用,会立刻抛出 requests.exceptions.RequestException(比如 ConnectionError、Timeout),不重试就中断下载。得自己套一层重试,别依赖 requests.adapters.HTTPAdapter 的默认重试——它默认只对 5xx 响应重试,对连接失败、超时完全不生效。

推荐用 tenacity 库,比手写 while 循环更可控:

from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
import requests
<p>@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=1, max=10),
retry=retry_if_exception_type((requests.ConnectionError, requests.Timeout))
)
def download_with_retry(url):
return requests.get(url, timeout=10)</p>

注意三点:
• stop_after_attempt(3) 表示最多请求 3 次(首次 + 2 次重试)
• wait_exponential 避免雪崩,第二次重试等待约 1 秒,第三次约 2 秒
• 显式指定 retry_if_exception_type,否则 404 这类 HTTP 错误也会被重试,不合理

HTTP 状态码 4xx/5xx 怎么区分处理?

重试机制不该盲目重试所有错误响应。比如 404 是资源不存在,重试一百次也没用;503 是服务暂时不可用,值得重试;403 可能是权限问题,需要检查 headers 或 token。

在 requests.Response 返回后立即判断状态码,再决定是否 raise 异常触发重试:

response = download_with_retry(url)
if response.status_code == 404:
    raise FileNotFoundError(f"Resource not found: {url}")
elif response.status_code >= 500:
    # 5xx 触发重试(tenacity 会捕获并重试)
    response.raise_for_status()
elif response.status_code == 403:
    # 记录日志,不重试,人工介入更合适
    logger.warning("Forbidden access to %s", url)
    return None

关键点:
• 不要用 response.raise_for_status() 无差别抛异常,它对 4xx 也 raise,导致本不该重试的错误被重试
• 对 4xx 做分类:400/401/403/404 含义不同,处理策略必须差异化
• 5xx 可统一交给重试机制,但注意某些 5xx(如 501 Not Implemented)重试也没意义

大文件下载中断后如何续传?

单纯重试整个文件,遇到大文件(>100MB)极不友好。得支持 Range 请求,从断点继续下载。

python学习
python学习

Python 编程学习助手 skill,面向零基础用户。 当用户表达以下意图时触发: - 学习 Python 基础概念(变量、数据类型、循环、函数、类等) - 请求 Python 练习题或测验 - 请求审查或调试 Python 代码 - 请求 Python 项目实战指导(爬虫、数据分析、Web 开发等入门项目) - 泛化表述如"教我 Python""我想学编程""Python 怎么写""帮我学 Python" 此 skill

下载

核心是检查服务器是否支持断点续传(看响应头是否有 Accept-Ranges: bytes),然后读取本地已下载文件长度,用 headers={"Range": f"bytes={file_size}-"} 发起请求:

def resume_download(url, filepath):
    headers = {}
    if os.path.exists(filepath):
        file_size = os.path.getsize(filepath)
        headers["Range"] = f"bytes={file_size}-"
<pre class="brush:python;toolbar:false;">response = requests.get(url, headers=headers, stream=True, timeout=30)

# 206 Partial Content 表示续传成功;200 表示不支持续传,需清空重下
if response.status_code == 206:
    mode = "ab"  # 追加写
elif response.status_code == 200 and os.path.exists(filepath):
    os.remove(filepath)  # 清掉残缺文件
    mode = "wb"
else:
    response.raise_for_status()

with open(filepath, mode) as f:
    for chunk in response.iter_content(chunk_size=8192):
        f.write(chunk)

容易踩的坑:
• 忘记检查 response.status_code 就直接写文件,可能把 200 响应(完整新文件)追加到旧文件末尾,造成损坏
• Range 请求返回 200 而不是 206,说明服务器不支持续传,必须删旧文件重新下
• 没设 stream=True,大文件会全加载进内存,OOM 风险高

如何避免重试期间阻塞主线程?

同步重试在下载多个文件时会卡住整个程序。如果并发下载,得用异步或线程池控制并发数和超时。

用 concurrent.futures.ThreadPoolExecutor 最简单,配合 timeout 参数防止单个任务拖垮全局:

from concurrent.futures import ThreadPoolExecutor, TimeoutError
<p>def safe_download(url, filepath, timeout=60):
try:
response = download_with_retry(url)  # 带重试的同步函数
with open(filepath, "wb") as f:
f.write(response.content)
return True
except TimeoutError:
logger.error("Download timeout for %s", url)
return False
except Exception as e:
logger.error("Failed to download %s: %s", url, e)
return False</p><p>with ThreadPoolExecutor(max_workers=3) as executor:
futures = [
executor.submit(safe<em>download, url, f"file</em>{i}.bin")
for i, url in enumerate(urls)
]
results = [f.result(timeout=120) for f in futures]</p>

要点:
• max_workers 别设太高,HTTP 下载本身受带宽和服务器并发限制,设 3–5 通常够用
• 外层 timeout(submit 的 result timeout)必须设,否则某个死链会让整个 executor hang 住
• 日志里记录具体失败原因,而不是只写 “download failed”,排查时省一半力气

重试和续传逻辑本身不复杂,但组合起来容易漏掉状态码分支、文件完整性校验、磁盘空间预判这些实际生产环境里天天撞见的问题。尤其别在没验证服务器是否支持 Range 的情况下硬上续传——有些 CDN 直接忽略 Range 头,还返回 200,结果下出一堆错位文件。

Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!

相关专题

更多
python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

2023.07.20

1791

4

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

2023.07.25

4684

7

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.31

1749

3

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

2023.08.03

27797

23

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

3347

5

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

3347

5

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

1203

5

python合并两个列表
python合并两个列表

Python是一种强大的编程语言,具有许多方便的功能和工具。在Python中,有多种方法可以合并两个列表。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.10

596

4

python是前端还是后端
python是前端还是后端

Python属于前端也属于后端,其灵活性和丰富的生态系统使得开发人员能够在不同的领域中灵活运用。本专题为大家提供python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

2603

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Uvicorn官方安装与入门手册
Uvicorn官方安装与入门手册

共0课时 | 0人学习

Python uv官方项目管理指南
Python uv官方项目管理指南

共0课时 | 0人学习

Python uv官方安装与入门文档
Python uv官方安装与入门文档

共0课时 | 0人学习