Python 3.x使用requests库重定向包含Unicode字符的URL

5
我正在尝试使用Python 3.x中的requests.get()获取以下URL:http://www.finanzen.net/suchergebnis.asp?strSuchString=DE0005933931(该URL由具有搜索字符串DE0005933931的基本URL组成)。
请求会被重定向(通过HTTP状态代码301)到浏览器中的http://www.finanzen.net/etf/ishares_core_dax%AE_ucits_etf_de(其中包含带有字符0xAE ®的URL)。使用重定向后的URL和requests.get()一起使用也可以正常工作。
当尝试使用Python 2.7获取搜索字符串URL时,一切正常并且我会收到重定向响应,但使用Python 3.x时,我会收到以下错误:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xae in position 21: invalid start byte

测试此功能的代码片段如下:
import requests

url_1 = 'http://www.finanzen.net/suchergebnis.asp?strSuchString=LU0274208692'
# redirected to http://www.finanzen.net/etf/db_x-trackers_msci_world_index_ucits_etf_1c
url_2 = 'http://www.finanzen.net/suchergebnis.asp?strSuchString=DE0005933931'
# redirected to http://www.finanzen.net/etf/ishares_core_dax%AE_ucits_etf_de

print(requests.get(url_1).status_code)  # working
print(requests.get(url_2).status_code)  # error with Python 3.x

更多信息:

  • 我正在使用Python 3.6.3在Windows 7上工作,requests.__version__ = '2.18.4',但我使用其他Python版本(3.4、3.5)时也遇到了同样的错误。
  • 使用其他搜索字符串,Python 3.x也可以正常工作,例如:http://www.finanzen.net/suchergebnis.asp?strSuchString=LU0274208692
  • 有趣的是,即使在https://www.hurl.it上尝试获取上述URL时,我仍然会收到一个“内部服务器错误”。也许这不是Python的问题。

你有任何想法,为什么Python 2.7可以工作,而Python 3.x却不能?我该怎么办?


1
你使用的是Linux还是Mac? - Mahesh Karia
1
requests.get("http://www.finanzen.net/etf/ishares_core_dax%AE_ucits_etf_de") 返回了成功的响应信息 (<Response [200]>),我刚在 Python 3.6.3 上进行了测试,使用的是 requests 版本 2.18.4。 - Tomalak
1
是的,这个可以。但是使用搜索URL不起作用。我已经将这个添加到问题中了。 - bastelflp
我正在使用Windows 7,已添加到问题中。 - bastelflp
1
是的,我可以确认搜索URL对我返回了相同的错误。 - Tomalak
1个回答

5
服务器响应的URL采用Latin-1编码且未进行URL编码;非ASCII字符以0x??十六进制转义的形式显示。
Location: /etf/ishares_core_dax0xAE_ucits_etf_de

那里的0xAE字节不是有效的URL字符;服务器在此违反了标准。他们应该发送的是:
Location: /etf/ishares_core_dax%AE_ucits_etf_de

或者

Location: /etf/ishares_core_dax%C2%AE_ucits_etf_de

使用转义数据对URL进行Latin-1或UTF-8编码。

我们可以修复requests,使其在出现此错误时更加健壮,通过返回未更改的Location头部:

from requests.sessions import SessionRedirectMixin

def get_redirect_target(
        self, resp, _orig=SessionRedirectMixin.get_redirect_target):
    try:
        return _orig(self, resp)
    except UnicodeDecodeError:
        return resp.headers['location']

SessionRedirectMixin.get_redirect_target = get_redirect_target

应用此补丁后,重定向按预期工作。

创建了一个拉取请求以改进位置处理。


1
根本原因在这里的设计决策:https://github.com/requests/requests/blob/master/requests/sessions.py#L106-L116 - Tomalak
1
@Tomalak:那个设计并没有错。服务器在位置中发送了一个无效的URL。 - Martijn Pieters
2
@Tomalak:设计可能需要更加健壮。 - Martijn Pieters
1
这显然是有问题的。你不能简单地交叉手指,没有任何错误处理就重新编码一个字符串。 - Tomalak
感谢@MartijnPieters的解释和补丁 - 它有效。尝试将其纳入请求本身是否有意义? - bastelflp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接