Python请求奇怪的URL%-Encoding

问题描述 投票:2回答:1

使用Python 3.6.1,请求2.13.0,我得到了所请求的URL的奇怪编码。我在查询字符串中有一个带有中文字符的URL,例如huà 話 用,它应该%-encode到hu%C3%A0%20%E8%A9%B1%20%E7%94%A8甚至hu%C3%A0+%E8%A9%B1+%E7%94%A8,但由于某种原因它是%-encoding到hu%C3%83%C2%A0%20%C3%A8%C2%A9%C2%B1%20%C3%A7%C2%94%C2%A8。这是不正确的。我一直在使用http://r12a.github.io/apps/conversion/页面来帮助我编写代码。我还使用了JavaScript encodeURI和PHP urlencode,并且没有得到任何接近我看到Requests库所做的事情。

我做错了什么,编码到目前为止?

更新:我查看了Mojibake编码并进一步挖掘了Requests库,发现问题所在,但我仍然不确定如何修复它。

我正在使用简单的.get(url)调用对内部服务器进行调用。该呼叫进入服务器并获得重定向响应。重定向页面在标题中有一个meta charset="UTF-8",其中列出的URL是正确的。离开服务器的location标题是可以的;它编码为UTF-8,Content-Type标题上有charset=UTF-8。但是,当我在Python中调试重定向响应时,我注意到响应对象上的标头不正确,似乎没有正确解码。 headers属性包含在locationhuÃ\xa0 話 ç\x94中。如上所述,它应解码为:huà 話 用。因此,奇怪的URL查询字符串由请求进行%编码并重新设置回服务器,服务器然后拒绝该URL(显然)。

有什么我可以做的,以防止请求搞砸了吗?或者让它正确解码location标头? Web浏览器似乎没有这个问题。

python python-3.x python-requests url-encoding mojibake
1个回答
4
投票

你有一个Mojibake编码。编码的字节是UTF-8字节的Latin-1解释的字节:

>>> from urllib.parse import quote
>>> text = 'huà 話 用'
>>> quote(text)
'hu%C3%A0%20%E8%A9%B1%20%E7%94%A8'
>>> quote(text.encode('utf8').decode('latin1'))
'hu%C3%83%C2%A0%20%C3%A8%C2%A9%C2%B1%20%C3%A7%C2%94%C2%A8'

您可以通过再次手动编码为Latin-1,然后从UTF-8解码来反转该过程:

>>> unquote('hu%C3%83%C2%A0%20%C3%A8%C2%A9%C2%B1%20%C3%A7%C2%94%C2%A8').encode('latin1').decode('utf8')
'huà 話 用'

或者您可以使用ftfy library自动修复错误的编码(ftfy通常做得更好,特别是当Windows代码页涉及Mo​​jibake时):

>>> from ftfy import fix_text
>>> fix_text(unquote('hu%C3%83%C2%A0%20%C3%A8%C2%A9%C2%B1%20%C3%A7%C2%94%C2%A8'))
'huà 話 用'

你这是关于URL的来源说的:

离开服务器的位置标题是可以的;它被编码为UTF-8

这是你的问题,就在那里。 HTTP标头始终编码为Latin-1(*)。服务器必须将Location头设置为完全百分比编码的URL,以便所有UTF-8字节都表示为%HH转义序列。这些只是ASCII字符,完全保存在Latin-1上下文中。

如果您的服务器将标头作为未转义的UTF-8字节发送,那么HTTP客户端(包括requests)将解码为Latin-1而不是导致您观察到的确切的Mojibake问题。由于URL包含无效的URL字符,因此requests将Mojibake结果转义为百分比编码版本。


(*)实际上,Location标题应该是absoluteURI as per RFC2396,它始终是ASCII(7位)干净数据,但由于其他一些HTTP标题允许“描述性”文本,因此Latin-1(ISO-8859-1)被接受标头数据的默认编码。在解码任何标头中的非ASCII数据时,请参阅TEXT rule in section 2.2 of the HTTP/1.1 RFC,最终解码http.client module标头的requests遵循此RFC。只有在按照the Message Header Extensions RFC 2047包装时才能提供非Latin-1数据,但这不适用于Location标头。

© www.soinside.com 2019 - 2024. All rights reserved.