Python请求奇怪的URL%-Encoding

时间:2017-04-03 15:32:06

标签: python python-3.x python-requests url-encoding mojibake

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

我做错了,编码到目前为止还没有?

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

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

我能做些什么来防止请求搞砸了吗?或者让它正确解码huà 話 用标题? Web浏览器似乎没有这个问题。

1 个答案:

答案 0 :(得分: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通常可以做得更好,尤其是当Mopibake中涉及Windows代码页时):

>>> 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à 話 用'

你这是关于网址的来源说的:

  

离开服务器的位置标头正常;它被编码为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)是标题数据的可接受默认编码。请参阅TEXT rule in section 2.2 of the HTTP/1.1 RFC,在解码任何标头中的非ASCII数据时,requests最终解码Location标头的http.client module遵循此RFC。只有按the Message Header Extensions RFC 2047包装时才能提供非Latin-1数据,但这不适用于[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\TimeProviders\NtpClient] "SpecialPollInterval"=dword:0000012c 标题。