Python下载图片遇到403错误怎么设置User-Agent解决

周叙
周叙
发布时间:2026-11-01| 6人浏览过
原创

详细讲解在Python中使用urllib与requests下载图片时遭遇HTTP 403 Forbidden错误的根本原因,手把手教你如何正确配置User-Agent请求头,以及遭遇图片防盗链Referer、Token鉴权时的深度排查方法。

用 Python 脚本批量或单独下载网络图片时遇到 HTTP 403 Forbidden(禁止访问) 报错,最普遍的原因是请求客户端携带了暴露爬虫特征的默认标识(如 Python-urllib/3.x 或 python-requests/2.x),直接触发了目标服务器网关的反爬拦截规则。最直接有效的解决办法是在发起请求时自定义 headers 字典,将 User-Agent 显式指定为主流桌面浏览器的标识字符串。本文将分别以 requests 库和 Python 标准库 urllib 为例,给出正确设置 User-Agent 并安全落盘保存图片的可运行完整代码,并深入辨析遭遇防盗链与鉴权时的排查路径。
Python下载图片遇到403错误怎么设置User-Agent解决
图 0:Python 下载图片 403 错误排查与请求头伪装架构概览

一、为什么 Python 默认下载图片会触发 403 错误?

HTTP 403 Forbidden 是 HTTP 协议中的标准状态码,表示“服务器已经理解客户端发起的请求,但拒绝执行或授权访问该资源”。当我们在网页浏览器中能够正常点击查看一张图片,而同一张图片在 Python 脚本中调用 urllib.request.urlopen() 或 requests.get() 时却返回 403 错误,根本症结就在于两者的 HTTP 请求头(Request Headers) 存在显著差异。

在不主动声明 headers 的情况下:

  • Python 原生标准库 urllib 默认发送的 User-Agent 字符串形如:Python-urllib/3.10(具体版本号随 Python 环境而定);
  • 第三方库 requests 默认发送的 User-Agent 字符串形如:python-requests/2.31.0。

现代网站的 Web 应用防火墙(WAF)、反向代理服务器(如 Nginx、OpenResty)或云厂商内容分发网络(CDN),普遍预置了基础的反爬虫与防盗链规则。一旦检测到入站请求中的 User-Agent 包含 Python、urllib、requests、curl 等关键字,或者该字段完全缺失,安全网关就会将其直接归类为非正常人类访问的自动化抓取脚本,并在网络层直接阻断,回传 HTTP 403 页面。

Python下载图片遇到403错误怎么设置User-Agent解决
图 1:默认客户端签名被网关拦截 vs 伪装浏览器请求头放行的原理对比

由此可见,解决此类问题的核心手段,就是在代码层面接管请求头构造,将 User-Agent 替换为主流合规浏览器的签名,使网络请求在服务器视角下看起来与常规浏览器访问完全一致。

二、使用 requests 库设置 User-Agent 下载图片(工程首选)

在实际生产和脚本开发中,推荐优先使用第三方库 requests。它不仅语法清晰易懂,而且原生支持 stream=True(流式响应传输),非常适合大文件和高分辨率图片的内存友好型下载。

2.1 核心步骤与实现逻辑

  1. 定义请求头字典:准备一个标准的桌面浏览器 User-Agent 字符串并封装在字典中;
  2. 开启流式请求:调用 requests.get(url, headers=headers, stream=True, timeout=15) 发起连接;
  3. 严格检查状态码:确认 response.status_code == 200,杜绝将 403 拒绝页面或 404 页面误写入图片;
  4. 分块写入本地文件:以 'wb'(二进制写入)模式创建文件,利用 response.iter_content() 逐块落盘。
Python下载图片遇到403错误怎么设置User-Agent解决
图 2:基于 requests 设置 User-Agent 流式下载图片的标准五步流水线

2.2 完整代码示例

以下是一段经过生产验证的标准图片下载函数。代码统一了变量命名,并加入了防御性异常捕获:

import os
import requests

def download_image_with_requests(image_url: str, save_path: str) -> bool:
    """
    使用 requests 库配置 User-Agent 下载图片并安全存盘
    
    :param image_url: 图片的网络 URL 目标地址
    :param save_path: 本地保存的目标完整路径(如 ./download/test.jpg)
    :return: 下载并保存成功返回 True,失败返回 False
    """
    # 1. 模拟真实 Chrome 浏览器的 User-Agent
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
                      '(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
    }

    try:
        # 2. 发起流式网络请求,设置超时时间防止挂起
        with requests.get(image_url, headers=headers, stream=True, timeout=15) as response:
            # 3. 校验服务端状态码,只有 200 才能继续处理
            if response.status_code != 200:
                print(f"[下载失败] HTTP 状态码异常: {response.status_code}, URL: {image_url}")
                return False

            # 确保目标上级目录存在
            parent_dir = os.path.dirname(save_path)
            if parent_dir and not os.path.exists(parent_dir):
                os.makedirs(parent_dir, exist_ok=True)

            # 4. 以二进制只写模式 ('wb') 分块落盘,推荐块大小为 8192 字节
            with open(save_path, 'wb') as f:
                for chunk in response.iter_content(chunk_size=8192):
                    if chunk:
                        f.write(chunk)

            # 5. 校验本地文件是否存在且非空
            if os.path.exists(save_path) and os.path.getsize(save_path) > 0:
                print(f"[下载成功] 图片已成功保存至: {save_path} ({os.path.getsize(save_path)} 字节)")
                return True
            else:
                print(f"[下载失败] 保存的文件为空: {save_path}")
                return False

    except requests.exceptions.RequestException as err:
        print(f"[网络异常] 请求发生错误: {err}")
        return False

# 示例调用
if __name__ == '__main__':
    target_url = "https://images.unsplash.com/photo-1579783900882-c0d3dad7b119"
    local_output = "./download_demo_requests.jpg"
    download_image_with_requests(target_url, local_output)
核心细节说明

很多初学者会写 with open(path, 'w') as f: f.write(response.text),这是严重的错误。图片本质是由 0 和 1 构成的二进制字节流,必须使用 'wb' 模式写入字节。如果使用 .text,requests 会尝试按文本编码(如 UTF-8)对字节进行解码转换,必然会损坏图像元数据,导致保存出来的图片打不开或损坏。

三、使用 Python 标准库 urllib 设置 User-Agent(零依赖环境)

如果运行环境无法通过 pip install requests 安装第三方包(例如受限的轻量级容器或企业离线服务器),也可以使用 Python 内置的标准库 urllib.request 达成同样的效果。

3.1 标准库的常见踩坑点:不要使用 urlretrieve

部分教程会推荐直接调用快捷方法 urllib.request.urlretrieve(url, filename)。然而,该快捷函数不支持直接传入 headers 参数,默认依然发送 Python-urllib 请求头,因而在开启防爬的站点上依然会遭遇 403 错误。

正确的做法是:使用 urllib.request.Request 显式构造一个包含了 headers 的请求对象,再交由 urllib.request.urlopen() 打开并读取数据。

3.2 urllib 完整可运行代码

import os
import urllib.request
import urllib.error

def download_image_with_urllib(image_url: str, save_path: str) -> bool:
    """
    使用 Python 标准库 urllib 配置 User-Agent 下载图片并保存
    
    :param image_url: 图片目标 URL
    :param save_path: 本地文件保存路径
    :return: 成功返回 True,失败返回 False
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
                      '(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
    }

    # 1. 显式封装包含 User-Agent 的 Request 对象
    req = urllib.request.Request(url=image_url, headers=headers)

    try:
        # 2. 发起请求并读取响应
        with urllib.request.urlopen(req, timeout=15) as response:
            if response.status != 200:
                print(f"[urllib 失败] 状态码: {response.status}")
                return False

            parent_dir = os.path.dirname(save_path)
            if parent_dir and not os.path.exists(parent_dir):
                os.makedirs(parent_dir, exist_ok=True)

            # 3. 循环分块读取并写入本地二进制文件
            with open(save_path, 'wb') as f:
                while True:
                    chunk = response.read(8192)
                    if not chunk:
                        break
                    f.write(chunk)

            if os.path.exists(save_path) and os.path.getsize(save_path) > 0:
                print(f"[urllib 成功] 文件保存至: {save_path}")
                return True
            return False

    except urllib.error.HTTPError as err:
        print(f"[urllib HTTP 错误] 状态码: {err.code}, 原因: {err.reason}")
        return False
    except urllib.error.URLError as err:
        print(f"[urllib URL 错误] 无法连接主机: {err.reason}")
        return False

# 示例调用
if __name__ == '__main__':
    target_url = "https://images.unsplash.com/photo-1579783900882-c0d3dad7b119"
    local_output = "./download_demo_urllib.jpg"
    download_image_with_urllib(target_url, local_output)

四、认知辨析:设置了 User-Agent 是否就一定能解决 403 错误?

网络上常有一种说法:“只要在请求里加了浏览器 User-Agent,所有的 403 错误都能迎刃而解。”

这种说法在什么条件下成立? 当且仅当目标服务器仅配置了最基础的静态客户端黑名单规则(即仅仅匹配 User-Agent 字符串是否包含 Python 关键字)时,该方案可以 100% 解决问题。对于初学者爬取普通博客、一般内容站的公开图片,这一招通常立竿见影。

哪里需要修正? 在实际复杂的网络环境中,HTTP 403 的实质是服务器拒绝向当前请求授权,而 User-Agent 仅仅声明了“我是什么软件”,并不能证明“我拥有读取该资源的权限”。如果服务端配置了更高级的防护,单纯伪装 User-Agent 依然会持续返回 403。

Python下载图片遇到403错误怎么设置User-Agent解决
图 3:已配置 User-Agent 但依然报 403 错误时的根因排查与决策路径

4.1 排查支线一:图片防盗链机制(Referer 限制)

这是仅次于 User-Agent 的第二大常见 403 原因。各大内容平台(如微博、知乎、微信公众号、小红书等)为了防止外部网站无偿消耗自身图床流量,均配置了 CDN 防盗链策略。

  • 鉴权原理:服务器检查请求头中的 Referer 字段(表示从哪个网页跳转过来的)。如果 Referer 为空,或者不在其信任的域名白名单内,直接回传 403。
  • 对策:在 headers 字典中显式加入该网站的主站地址或图片所属的文章页面地址,例如:
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...',
        'Referer': 'https://www.target-website.com/'  # 填入图片来源站点的域名或文章URL
    }
    

4.2 排查支线二:时效性签名参数(Token / Signature)失效

很多现代云存储(如阿里云 OSS、腾讯云 COS、AWS S3)生成的原图链接带有动态鉴权参数,如 ?sign=xxx&expires=1710000000。

  • 表现:刚从网页上右键复制的 URL 能下载,但几分钟或几小时后再次运行代码就报错 403。
  • 对策:确认该签名是否具有时效性。如果是动态签名,爬虫程序必须即时通过页面或 API 重新提取最新生成的临时 URL,立即发起下载,不可将过期 URL 存入数据库长期使用。

4.3 排查支线三:用户登录权限(Cookie / Authorization)

某些高质量图片、高清原图或私密社群资源,必须在用户登录认证后方可查看。未携带有效登录态凭证的请求会被重定向或直接阻断为 403。

  • 对策:在浏览器开发者工具(F12)的 Network 面板中,找到对应图片的请求头,将 Cookie 字段复制添加到 Python 代码的 headers 字典中;或者使用 requests.Session() 先完成模拟登录,再复用会话发起下载。

4.4 排查支线四:高频请求触发了 IP 限额风控

如果单独下载前几张图片都很顺利,而在循环批量下载数百张图片时中途突然密集爆发 403 错误,这通常意味着请求频次触发了服务器的速率限制(Rate Limiting)规则,你的当前出口 IP 被临时加入限流黑名单。

  • 对策:在每次下载之间加入适度休眠(如 time.sleep(random.uniform(1.0, 2.5)));控制并发线程数量;或者为 requests.get() 配置代理 IP 池(proxies={'http': '...', 'https': '...'})。

五、核心实践与防坑总结

将 Python 下载图片的代码从“能跑通”提升到“具备生产级鲁棒性”,需要牢记以下几个关键维度:

Python下载图片遇到403错误怎么设置User-Agent解决
图 4:Python 图片下载 403 规避与工程健壮性总结清单
技术关注点 常见错误做法 推荐标准规范
User-Agent 设置 缺失或使用简写(如 'User-Agent': 'test') 使用标准 Chrome/Safari 完整标识字符串
防盗链处理 仅设置 User-Agent,忽略 Referer 成对设置 User-Agent 与来源站 Referer
文件落盘模式 使用 open(..., 'w') 写入 response.text 严格使用 'wb' 模式写入二进制 iter_content()
状态码校验 不管三七二十一直接落盘,存下 403 网页 HTML 明确判断 status_code == 200 再开流写入
网络鲁棒性 未指定 timeout,遇到网络丢包永久卡死 显式传入 timeout=15 并包裹 try-except 捕获异常

通过在代码中标准规范地注入浏览器 User-Agent、结合目标场景按需补充 Referer、并在文件写入前做好状态码断言,即可从容解决绝大多数 Python 下载图片时的 403 报错问题。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。