Requests: Response.text 返回未正确解码的文本(请求 1.2.3,python 2.7)

创建于 2013-09-15  ·  24评论  ·  资料来源: psf/requests

如果 http 服务器返回 Content-type: text/* 没有编码,Response.text 总是将其解码为 'ISO-8859-1' 文本。

它可能在 RFC2616/3.7.1 中有效,但这在 2013 年的现实生活中是错误的。

我用中文制作了示例页面:
http://lavr.github.io/python-emails/tests/requests/some-utf8-text.html
所有浏览器都正确呈现此页面。
但 reguests.get 返回无效文本。

这是使用该网址的简单测试:
https://gist.github.com/lavr/6572927

最有用的评论

@lavr (/cc @sigmavirus24),比这更简单,您可以简单地自己提供编码。

>>> r = requests.get('http://irresponsible-server/')
>>> r.encoding = 'utf-8'

然后,正常进行。

所有24条评论

感谢这个@lavr!

这是请求的深思熟虑的设计决策。 我们一直在遵循规范,除非我们发现自己处于规范与现实世界行为如此大相径庭的位置,以至于它成为一个问题(例如,在对 POST 的 302 响应之后的 GET)。

如果上游服务器知道正确的编码是什么,它应该发出信号。 否则,我们将遵循规范所说的。 =)

如果您认为规范默认值不好,我强烈建议您参与 HTTP/2.0 的 RFC 流程,以便更改此默认值。 =)

@Lukasa所说的 + 事实是,如果从标头中检索到的编码不存在,我们将依靠字谜来猜测编码。 由于字符很少,charade 不会返回任何明确的信息,因为它使用统计数据来猜测正确的编码是什么。

坦率地说,年份没有区别,也不会改变规格。

如果你知道你期望的是什么编码,你也可以像这样自己解码:

text = str(r.content, '<ENCODING>', errors='replace')

就我而言,请求没有任何问题,这也不是字谜中的错误。 由于@Lukasa似乎同意我的

@lavr (/cc @sigmavirus24),比这更简单,您可以简单地自己提供编码。

>>> r = requests.get('http://irresponsible-server/')
>>> r.encoding = 'utf-8'

然后,正常进行。

@kennethreitz令人失望。 为什么我们要让人们这么容易? =P

绝对地 :)

主要用于日本网站。 他们都在他们的编码上撒谎。

@西格玛病毒24
请注意,utils.get_encoding_from_headers 总是返回'ISO-8859-1',并且charade 没有机会被调用。
所以错误是:我们期望使用 charade 来猜测编码,但事实并非如此。

上面的补丁修复了一个错误,但仍遵循 RFC。
请考虑审查它。

@lavr抱歉,我们没有说清楚。 我们_不_期望在这种情况下调用 charade。 RFC 非常清楚:如果您不指定字符集,并且 MIME 类型为text/* ,则必须假定编码为 ISO-8859-1。 这意味着“不要猜测”。 =)

@lavr :只需将r.encodingNone ,它就会按您的预期工作(我认为)。

或者做r.encoding = r.apparent_encoding

甚至更好。

r.encoding = Noner.encoding = r.apparent_encoding我们丢失了服务器字符集信息。
我认为,完全忽略服务器标头并不是一个好的解决方案。

正确的解决方案是这样的:

r = requests.get(...)
params = cgi.parse_header(r.headers.get('content-type'))[0]
server_encoding = ('charset' in params) and params['charset'].strip("'\"") or None
r.encoding = server_encoding or r.apparent_encoding
text = r.text

看起来很奇怪:(

或者这样做:

r = requests.get(...)

if r.encoding is None or r.encoding == 'ISO-8859-1':
    r.encoding = r.apparent_encoding

我不这么认为:)

条件r.encoding is None没有意义,因为对于 content-type=text/*,r.encoding 永远不可能是 None。

r.encoding == 'ISO-8859-1' ...是什么意思? 服务器发送字符集='ISO-8859-1' 或服务器未发送字符集? 如果首先,我不应该猜测字符集。

@lavr我正在讨论非文本基础。 您可以使用以下条件来排除charset可能性:

r.encoding == 'ISO-8859-1' and not 'ISO-8859-1' in r.headers.get('Content-Type', '')

@卢卡萨
嗯,我可以使用这个黑客。
东欧和亚洲的每个人都可以使用它。

但是如果我们在请求中修复它呢? ;)
如果请求可以在没有字符集的情况下诚实地设置enconding=None响应呢?

正如我们多次讨论过的,Requests 完全遵循 HTTP 规范。 目前的行为并没有错。 =)

它对您的用例没有帮助的事实是另一回事。 =)

好了,讨论到此就够了。 感谢您的反馈。

更新的 HTTP 1.1 废弃了 ISO-8859-1 默认字符集: http :

我们已经在#2086 中跟踪了这一点。 =)

它可能关心的人,这里是一个兼容性补丁

使用以下代码创建文件requests_patch.py并导入它,那么问题应该解决了。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import chardet

def monkey_patch():
    prop = requests.models.Response.content
    def content(self):
        _content = prop.fget(self)
        if self.encoding == 'ISO-8859-1':
            encodings = requests.utils.get_encodings_from_content(_content)
            if encodings:
                self.encoding = encodings[0]
            else:
                self.encoding = chardet.detect(_content)['encoding']

            if self.encoding:
                _content = _content.decode(self.encoding, 'replace').encode('utf8', 'replace')
                self._content = _content
                self.encoding = 'utf8'

        return _content
    requests.models.Response.content = property(content)

monkey_patch()


@lavr (/cc @sigmavirus24),比这更简单,您可以简单地自己提供编码。

>>> r = requests.get('http://irresponsible-server/')
>>> r.encoding = 'utf-8'

然后,正常进行。

谢谢你! 关于如何在一行中做到这一点的任何想法?

此页面是否有帮助?
0 / 5 - 0 等级