python 3.7 urllib.request 不遵循重定向 URL

Posted

技术标签:

【中文标题】python 3.7 urllib.request 不遵循重定向 URL【英文标题】:python 3.7 urllib.request doesn't follow redirect URL 【发布时间】:2020-10-04 14:48:30 【问题描述】:

我正在使用带有 urllib 的 Python 3.7。 一切正常,但当它收到 http 重定向请求 (307) 时似乎不会自动重定向。

这是我得到的错误:

ERROR 2020-06-15 10:25:06,968 HTTP Error 307: Temporary Redirect

我必须使用 try-except 来处理它并手动向新位置发送另一个请求:它工作正常,但我不喜欢它。

这些是我用来执行请求的代码:

      req = urllib.request.Request(url)
      req.add_header('Authorization', auth)
      req.add_header('Content-Type','application/json; charset=utf-8')
      req.data=jdati  
      self.logger.debug(req.headers)
      self.logger.info(req.data)
      resp = urllib.request.urlopen(req)

url 是一个 https 资源,我设置了一个带有一些授权信息和内容类型的标题。 req.data 是一个 JSON

从 urllib 文档中,我了解到重定向是由库本身自动执行的,但它对我不起作用。它总是引发 http 307 错误并且不遵循重定向 URL。 我也尝试使用指定默认重定向处理程序的开启程序,但结果相同

  opener = urllib.request.build_opener(urllib.request.HTTPRedirectHandler)          
  req = urllib.request.Request(url)
  req.add_header('Authorization', auth)
  req.add_header('Content-Type','application/json; charset=utf-8')
  req.data=jdati  
  resp = opener.open(req)         

可能是什么问题?

【问题讨论】:

您访问的 URL 是什么? (不是您要重定向到的那个。) 我在 RFC 2616 上找到了这个:10.3.8 307 临时重定向 如果收到 307 状态代码以响应 GET 或 HEAD 以外的请求,则用户代理 不得自动重定向请求,除非用户可以确认,因为这可能会改变发出请求的条件。会不会是这个原因? @calabrone 好声音。引用 HTTPRequestHandler 的 Python 文档:“某些 HTTP 重定向需要来自此模块的 client 代码的操作。如果是这种情况,则会引发 HTTPError。有关各种重定向代码的精确含义的详细信息,请参阅 RFC 2616 。” (我的重点)——所以你很可能对 307 是正确的。在这种情况下,你的 try-except 可能算作“用户确认”......(docs.python.org/3/library/…) @mrblewog 是一个 https 网址。由于我的商业政策,我不想分享它。 不用担心——正如你在另一条评论中所说,这可能是对 307 的特殊处理。 【参考方案1】:

在 cmets 部分的讨论中,您已经正确识别了未自动完成重定向的原因。具体来说,RFC 2616, Section 10.3.8 指出:

如果收到 307 状态代码以响应其他请求 与 GET 或 HEAD 相比,用户代理不能自动重定向 请求,除非它可以被用户确认,因为这可能 更改发出请求的条件。

回到问题 - 鉴于已分配 data,这会自动导致 get_method 返回 POST(根据 how this method was implemented),并且由于请求方法是 POST,并且响应代码是307,而是根据上述规范提出HTTPError。在 Python 的 urllib 的上下文中,urllib.request 模块的 this specific section 会引发异常。

对于实验,请尝试以下代码:

import urllib.request
import urllib.parse


url = 'http://httpbin.org/status/307'
req = urllib.request.Request(url)
req.data = b'hello'  # comment out to not trigger manual redirect handling
try:
    resp = urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
    if e.status != 307:
        raise  # not a status code that can be handled here
    redirected_url = urllib.parse.urljoin(url, e.headers['Location'])
    resp = urllib.request.urlopen(redirected_url)
    print('Redirected -> %s' % redirected_url)  # the original redirected url 
print('Response URL -> %s ' % resp.url)  # the final url

按原样运行代码可能会产生以下结果

Redirected -> http://httpbin.org/redirect/1
Response URL -> http://httpbin.org/get 

注意后续重定向到get 是自动完成的,因为后续请求是GET 请求。注释掉 req.data 赋值行将导致缺少“重定向”输出行。

在异常处理块中需要注意的其他值得注意的事情,e.read() 可以用来检索服务器生成的响应正文,作为HTTP 307 响应的一部分(由于发布了data,可能会有一个简短的响应中可能被处理的实体?),并且需要 urljoin,因为 Location 标头可能是后续资源的相对 URL(或只是缺少主机)。

此外,出于兴趣(以及出于链接目的),这个特定问题之前已被多次问过,我很惊讶他们从未得到任何答案,如下:

How to handle 307 redirection using urllib2 from http to https HTTP Error 307: Temporary Redirect in Python3 - INTRANET HTTP Error 307 - Temporary redirect in python script

【讨论】:

以上是关于python 3.7 urllib.request 不遵循重定向 URL的主要内容,如果未能解决你的问题,请参考以下文章

Python 3.7 urllib.request 重新使用 &nbsp 而不是内容

Python Spider - urllib.request

python中urllib.request对象案例

爬虫小探-Python3 urllib.request获取页面数据

Python 3.5 urllib.request 403 禁止错误

urllib.request.Request