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

python 3.7 urllib.request doesn't follow redirect URL

我正在使用 Python 3.7 和 urllib。 一切正常,但当它收到 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 资源,我设置了一个带有一些授权信息的 header 和 content-type。 req.data 是一个 JSON

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

  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)         

可能是什么问题?

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

If the 307 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.

回到问题 - 鉴于 data 已分配,这会自动导致 get_method 返回 POST(根据 [=19= 的 how this method was implemented), and since that the request method is POST, and the response code is 307, an HTTPError is raised instead as per the above specification. In the context of Python's urllib, 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() 可以用来检索服务器生成的响应 body 作为 HTTP 307 响应的一部分(因为 data 已发布,响应中可能有一个可以处理的短实体?),并且 urljoin 是必需的,因为 Location header 可能是相对的 URL(或只是缺少主机)到后续资源。

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