如何在 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.re
quest.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配置与解决方案


quest.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}")