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は無効なテキストを返します。

そして、ここにそのURLを使った簡単なテストがあります:
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が言ったこと+ヘッダーから取得したエンコーディングが存在しない場合、エンコーディングを推測するためにシャレードに依存しているという推測するため、明確なものは返されません。

率直に言って、年は違いがなく、仕様も変更されません。

期待しているエンコーディングがわかっている場合は、次のように自分でデコードすることもできます。

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

私に関する限り、リクエストに問題はなく、これもシャレードのバグではありません。 @Lukasaは私に同意しているようですので、これを閉じます。

@lavr (/ cc @ sigmavirus24)は、それよりもさらに簡単で、自分でエンコーディングを提供するだけです。

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

その後、通常どおりに進みます。

@kennethreitzそれは残念です。 なぜ私たちはそれを人々にとって簡単にするのですか? = P

絶対 :)

主に日本のウェブサイト向けです。 それらはすべて、エンコーディングについて嘘をついています。

@ sigmavirus24
utils.get_encoding_from_headersは常に「ISO-8859-1」を返し、charadeが呼び出される可能性がないことに注意してください。
つまり、バグは次のとおりです。エンコーディングを推測するためにシャレードが使用されると予想されますが、そうではありません。

上記のパッチはバグを修正しますが、RFCに準拠しています。
確認してください。

@lavr申し訳ありませんが、これはあまり明確にしませんでした。 この場合、シャレードが呼び出されることは期待していません。 RFCは非常に明確です。文字セットを指定せず、MIMEタイプがtext/*場合、エンコーディングはISO-8859-1であると想定する必要があります。 それは「推測しない」という意味です。 =)

@lavrr.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

私はそうは思いません:)

content-type = text / *のr.encodingをNoneにすることはできないため、条件r.encoding is Noneは意味がありません。

r.encoding == 'ISO-8859-1' ...それはどういう意味ですか? サーバーはcharset = '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仕様に従っています。 現在の動作は間違っていません。 =)

それがあなたのユースケースに役立たないという事実は全く別の話です。 =)

了解しました。これについてはこれで十分です。 フィードバックをお寄せいただきありがとうございます。

更新されたHTTP1.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 評価