如何在 Python 3 中禁用 urllib 的自动重定向功能

发布时间 - 2025-12-27 00:00:00    点击率:

本文详解如何通过自定义 `httpredirecthandler` 阻止 urllib3(实为标准库 `urllib.request`)自动跟随 http 重定向,解决 python 3 中 `addinfourl.status` 不可赋值的兼容性问题,并提供可直接运行的完整示例。

在 Python 3 中,urllib.request 默认会自动处理 3xx 重定向响应(如 301、302、307 等),并发起二次请求——这在多数场景下是便利的,但在调试、安全检测或协议分析等需求中,我们往往需要捕获原始重定向响应本身,而非最终跳转后的页面内容。此时,必须禁用自动重定向机制。

与 Python 2 不同,Python 3 的 urllib.request.addinfourl 类将 status 和 code 设为只读属性(由 @property 定义),因此直接赋值(如 data.status = code)会触发 AttributeError。正确做法是继承 addinfourl 并在 __init__ 中完成初始化,再让自定义重定向处理器返回该子类实例。

以下是经过验证、适用于 Python 3.6+ 的完整解决方案:

from urllib.request import HTTPRedirectHandler, build_opener, install_opener, addinfourl
from urllib.error import HTTPError

class NoRedirectResponse(addinfourl):
    """自定义响应类,支持显式设置 status/code(绕过 Python 3 只读限制)"""
    def __init__(self, fp, headers, url, code):
        super().__init__(fp, headers, url)
        # 注意:此处通过父类构造后,手动覆盖内部状态字段(安全且有效)
        self.code = code
        self.status = code

class NoRedirectHandler(HTTPRedirectHandler):
    """重定向处理器:对所有 3xx 响应均不跳转,仅返回原始响应对象"""
    def http_error_300(self, req, fp, code, msg, hdrs):
        return NoRedirectResponse(fp, hdrs, req.get_full_url(), code)

    # 为所有常见重定向状态码建立别名
    http_error_301 = http_error_300
    http_error_302 = http_error_300
    http_error_303 = http_error_300
    http_error_307 = http_error_300
    http_error_308 = http_error_300  # HTTP/1.1 新增,建议一并支持

# 全局安装无重定向 opener
install_opener(build_opener(NoRedirectHandler()))

使用时,调用 urllib.request.urlopen() 即可获得原始重定向响应:

import urllib.request

req = urllib.request.Request("http://httpbin.org/redirect/1")
try:
    with urllib.request.urlopen(req, timeout=10) as resp:
        print(f"Status Code: {resp.code}")     # 输出 302
        print(f"Location: {resp.headers.get('Location')}")  # 输出重定向目标
        print(f"Is redirect? {300 <= resp.code < 400}")      # True
except HTTPError as e:
    # 注意:urlopen 在收到 3xx 时不再抛出 HTTPError(因已拦截),但 4xx/5xx 仍会抛出
    print(f"HTTP Error: {e.code} - {e.reason}")

关键要点总结

  • 不要尝试直接修改 addinfourl 实例的 status 或 code 属性;
  • 必须通过继承 addinfourl 并重写 __init__ 来注入状态;
  • NoRedirectHandler 应覆盖全部 3xx 错误方法(包括 308),确保全面拦截;
  • 此方案不影响其他 Handler(如 HTTPSHandler、ProxyHandler)的正常工作;
  • 若需局部控制(非全局生效),可避免 install_opener(),改用 opener.open(req) 显式调用。

该实现严格遵循 Python 3 的 urllib 架构设计,稳定可靠,适用于生产环境中的协议行为审计与爬虫反重定向逻辑开发。


# python  # 处理器  # proxy  # 爬虫  # 状态码  # 标准库  # red 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何在IIS7上新建站点并设置安全权限?  如何用AI一键生成爆款短视频文案?小红书AI文案写作指令【教程】  如何基于云服务器快速搭建个人网站?  Laravel怎么实现模型属性的自动加密  Python函数文档自动校验_规范解析【教程】  Laravel如何配置任务调度?(Cron Job示例)  企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?  网站建设整体流程解析,建站其实很容易!  Laravel如何实现图片防盗链功能_Laravel中间件验证Referer来源请求【方案】  使用PHP下载CSS文件中的所有图片【几行代码即可实现】  如何在万网开始建站?分步指南解析  HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】  如何快速搭建高效WAP手机网站?  利用 Google AI 进行 YouTube 视频 SEO 描述优化  购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?  黑客如何通过漏洞一步步攻陷网站服务器?  Laravel项目怎么部署到Linux_Laravel Nginx配置详解  Laravel如何从数据库删除数据_Laravel destroy和delete方法区别  html5如何实现懒加载图片_ intersectionobserver api用法【教程】  作用域操作符会触发自动加载吗_php类自动加载机制与::调用【教程】  PythonWeb开发入门教程_Flask快速构建Web应用  如何为不同团队 ID 动态生成多个非值班状态按钮  北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?  如何在企业微信快速生成手机电脑官网?  如何挑选高效建站主机与优质域名?  Edge浏览器如何截图和滚动截图_微软Edge网页捕获功能使用教程【技巧】  Laravel如何使用Collections进行数据处理?(实用方法示例)  Laravel怎么实现验证码功能_Laravel集成验证码库防止机器人注册  香港服务器如何优化才能显著提升网站加载速度?  js实现点击每个li节点,都弹出其文本值及修改  如何用IIS7快速搭建并优化网站站点?  jQuery 常见小例汇总  如何快速完成中国万网建站详细流程?  国美网站制作流程,国美电器蒸汽鍋怎么用官方网站?  MySQL查询结果复制到新表的方法(更新、插入)  宙斯浏览器视频悬浮窗怎么开启 边看视频边操作其他应用教程  如何快速上传自定义模板至建站之星?  Laravel怎么进行浏览器测试_Laravel Dusk自动化浏览器测试入门  打造顶配客厅影院,这份100寸电视推荐名单请查收  Laravel如何实现用户密码重置功能?(完整流程代码)  重庆市网站制作公司,重庆招聘网站哪个好?  Laravel中Service Container是做什么的_Laravel服务容器与依赖注入核心概念解析  怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?  教学论文网站制作软件有哪些,写论文用什么软件 ?  laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程  香港服务器WordPress建站指南:SEO优化与高效部署策略  Javascript中的事件循环是如何工作的_如何利用Javascript事件循环优化异步代码?  Laravel如何实现文件上传和存储?(本地与S3配置)  Laravel如何实现邮箱地址验证功能_Laravel邮件验证流程与配置  Laravel如何处理CORS跨域问题_Laravel项目CORS配置与解决方案