Python使用requests库下载单张图片的代码示例

Noah
Noah
发布时间:2026-10-30 | 8人浏览过
原创

本文提供使用Python requests库下载单张图片的完整代码示例,深入解析流式分块写入、请求头伪装、状态校验与异常排查等核心实践细节。

在 Python 中下载网络图片,最直接且工业级通用的解决方案是使用 requests 库发起 HTTP GET 请求,并将返回的原始二进制字节流写入本地文件。为避免大尺寸图片占用过多内存,规范的标准做法是开启流式模式(stream=True)并按块(Chunk)逐步写入磁盘。本文将提供清晰完整的代码实现,并拆解从网络请求到持久化存储的关键细节与注意事项。

一、下载核心原理与标准执行流程

在 HTTP 协议中,图片文件与普通的 JSON 或网页 HTML 不同,它由未经字符编码的原始二进制字节构成。下载图片的底层逻辑可以精炼为以下五个关键环节:

  1. 构建请求配置:明确目标图片 URL,配置防拦截的 User-Agent 标头以及合理的网络超时时限。
  2. 发起流式请求:调用 requests.get() 时开启 stream=True,该参数使客户端先接收 HTTP 响应头,暂不把正文全部灌入内存。
  3. 验证响应状态:通过 response.raise_for_status() 判断状态码是否为 200,并可选校验 Content-Type 是否为图片。
  4. 以二进制模式分块写入:使用 Python 内置的 open(..., 'wb') 打开目标路径,通过 response.iter_content() 设定固定缓冲区(如 128KB)循环写入。
  5. 安全关闭连接:利用 with 上下文管理器自动关闭本地文件描述符与底层 TCP 连接,确保文件写入完整且无资源泄漏。
Python使用requests库下载单张图片的代码示例
图 1:使用 Python requests 下载单张图片的代码调用与落地阶段分解

二、完整实战代码示例

以下代码提供了一个开箱即用、具备完备防御机制的函数 download_single_image。该示例已将请求头模拟、流式写入、多层异常捕获整合在一起:

import os
import requests

def download_single_image(image_url: str, save_path: str, timeout: int = 15) -> bool:
    """
    使用 requests 库下载单张图片并以二进制形式保存到指定本地文件。

    :param image_url: 图片的完整 HTTP/HTTPS 链接
    :param save_path: 保存到本地的目标文件路径(如 'images/sample.jpg')
    :param timeout: 超时时限(秒),防止由于网络阻塞导致程序无限挂起
    :return: 下载成功返回 True,发生任何异常返回 False
    """
    # 1. 模拟常见的现代浏览器请求头,避免大部分防爬虫与防盗链拦截
    headers = {
        "User-Agent": (
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 (KHTML, like Gecko) "
            "Chrome/124.0.0.0 Safari/537.36"
        ),
        "Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
    }

    try:
        # 2. 自动检查并创建目标存放目录,避免 FileNotFoundError
        directory = os.path.dirname(save_path)
        if directory and not os.path.exists(directory):
            os.makedirs(directory, exist_ok=True)

        # 3. 发起请求,启用 stream=True 模式
        with requests.get(image_url, headers=headers, stream=True, timeout=timeout) as response:
            # 遇到 404 Not Found 或 403 Forbidden 等状态码时,立即抛出 HTTPError 异常
            response.raise_for_status()

            # 4. 可选检查:确保返回的数据确实属于图像类型
            content_type = response.headers.get("Content-Type", "")
            if content_type and not content_type.startswith("image/"):
                print(f"提示: 返回的 Content-Type ({content_type}) 可能不是图片资源。")

            # 5. 以二进制写模式 ('wb') 打开文件,分块写入磁盘
            # 缓冲区大小推荐设为 128KB (131072 字节)
            with open(save_path, "wb") as f:
                for chunk in response.iter_content(chunk_size=131072):
                    if chunk:
                        f.write(chunk)

        print(f"下载成功: {save_path}")
        return True

    except requests.exceptions.Timeout:
        print(f"下载失败: 请求超时 (超过 {timeout} 秒未响应)")
    except requests.exceptions.HTTPError as err_h:
        print(f"下载失败: HTTP 状态错误 ({err_h})")
    except requests.exceptions.RequestException as err_r:
        print(f"下载失败: 网络通信异常 ({err_r})")
    except OSError as err_os:
        print(f"下载失败: 磁盘写入或路径错误 ({err_os})")

    return False

if __name__ == "__main__":
    # 调用示例:下载一张测试图片并保存为 local_banner.png
    target_url = "https://www.python.org/static/community_logos/python-logo-master-v3-TM.png"
    target_file = "downloaded_python_logo.png"

    success = download_single_image(target_url, target_file)
    if success:
        print("图片已妥善保存到当前工作目录。")
    else:
        print("请根据上述提示排查下载错误。")

代码核心要点解析

  • stream=True:请求发出后,连接处于保持状态,仅传输 HTTP 响应头;只有在迭代 response.iter_content() 时才从网络套接字逐步读取实际数据块。
  • mode="wb":必须以 wb(Binary Write)打开文件。如果误写为 w,Python 会将字节流按照系统默认文本编码(如 UTF-8)进行编解码,直接破坏 PNG/JPEG 的文件头魔数(Magic Number),导致图片彻底损坏无法打开。
  • response.raise_for_status():当服务器返回 404、403 或 500 等错误时,requests.get() 默认不会抛出异常,而是返回带有 HTML 报错内容的响应。若不加校验,程序会将这些 HTML 错误页直接写入 .jpg 文件,造成假成功。该语句能确保只有 200 系列状态码才进入写入环节。

三、写入机制对比:直接载入 vs 流式分块

部分初学者常习惯使用 open('pic.jpg', 'wb').write(response.content) 的方式来保存图片。虽然在图片仅有数十 KB 的情况下这种做法同样有效,但在通用代码和生产环境中,两者的内存吞吐特性有着显著差异:

Python使用requests库下载单张图片的代码示例
图 2:直接载入与流式分块写入在数据流与内存驻留层面的机制对比

chunk_size 的取舍建议:通常推荐设置为 64KB(65536 字节)至 128KB(131072 字节)。块太小(例如 1024 字节)会增加 Python 与操作系统之间的系统调用(Syscall)开销;块太大(如 16MB)则削弱了流式下载节省内存的初衷。

四、请求头伪装与网络交互细节

很多图片服务器部署了防盗链机制(Hotlinking Protection)或反爬虫防火墙。直接使用默认的 requests.get(url) 往往会触发 403 Forbidden,因为默认请求头中 User-Agent 为 python-requests/x.xx.x,容易被识别并拒绝服务。

Python使用requests库下载单张图片的代码示例
图 3:客户端与目标服务器在图片下载交互中的标头配置与流转模型

关键配置指导

  • User-Agent 伪装:必须配置。如果不配置,大部分公共图床会直接返回 403 Forbidden。
  • Referer 规避防盗链:部分网站(如微博图床、某些社交平台)要求 Referer 必须为指定域名。如果发现下载的文件提示 403,可尝试将 Referer 设置为该图片所在的来源网页地址。
  • 精细化 Timeout 控制:requests.get(..., timeout=(3.05, 15)) 允许传入一个元组:第一个数值表示 TCP 连接建立超时(通常设定略大于 3 秒,避免重试延迟),第二个数值表示数据包读取超时(针对大图建议设为 15 秒以上)。

五、常见异常排查与防御决策

网络环境充满了不确定性。无论是 DNS 解析失败、握手超时,还是服务器下发了非预期的 HTML 错误页,都需要建立一套清晰的排查分支策略:

Python使用requests库下载单张图片的代码示例
图 4:针对常见网络中断、鉴权拒绝与资源缺失的排查决策树

常见错误速查与处理表

现象 / 异常类型 根本原因 解决措施
HTTPError: 403 Forbidden 服务器识别到非浏览器客户端,或开启了图片防盗链 在 headers 中补充合法的 User-Agent 和与目标站点一致的 Referer
HTTPError: 404 Not Found 图片路径无效,或链接中包含了未处理的相对路径 / 特殊转义字符 在浏览器地址栏手动访问图片 URL,确认地址无误并处理 URL 编码
requests.exceptions.ReadTimeout 网络延迟过高或图片文件过大,超出指定秒数未收到任何后续数据 增大 timeout 阈值,或拆分为 (connect_timeout, read_timeout)
保存后的图片提示“文件损坏无法打开” 使用了普通文本模式 w 打开文件,或将 404/500 的 HTML 页面写入了图片 务必确认写入模式为 wb,并在写入前执行 response.raise_for_status()

六、总结与落地建议

使用 Python requests 库完成单张图片下载,核心要求是“稳健可靠”。总结起来有三个核心法则:

  1. 使用 stream=True 和 iter_content():把内存消耗锁死在较小的块缓冲区内,兼顾小图的灵活性与高分辨率大图的安全性。
  2. 务必指定二进制写入模式 open(..., 'wb'):切勿使用字符串默认写入,确保每一个原始图片字节原样落盘。
  3. 必须校验 raise_for_status() 并设置合理的 timeout:网络通信必须有边界意识,严防假成功与长时间进程挂起。

遵循以上规范,便可构建出兼顾安全性、高性能与高容错率的单张图片下载模块。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表