Requests: Response.text mengembalikan teks yang tidak didekodekan dengan benar (permintaan 1.2.3, python 2.7)

Dibuat pada 15 Sep 2013  ·  24Komentar  ·  Sumber: psf/requests

Jika server http mengembalikan Content-type: text/* tanpa encoding, Response.text selalu mendekodekannya sebagai teks 'ISO-8859-1'.

Ini mungkin valid di RFC2616/3.7.1, tetapi ini salah dalam kehidupan nyata di 2013.

Saya membuat halaman contoh dengan teks Cina:
http://lavr.github.io/python-emails/tests/requests/some-utf8-text.html
Semua browser merender halaman ini dengan benar.
Tetapi reguests.get mengembalikan teks yang tidak valid.

Dan inilah tes sederhana dengan url itu:
https://Gist.github.com/lavr/6572927

Komentar yang paling membantu

@lavr (/cc @sigmavirus24), bahkan lebih mudah dari itu, Anda cukup menyediakan pengkodean sendiri.

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

Kemudian, lanjutkan secara normal.

Semua 24 komentar

Terima kasih untuk @lavr ini!

Ini adalah keputusan desain yang disengaja untuk Permintaan. Kami mengikuti spesifikasi kecuali kami menemukan diri kami dalam posisi di mana spesifikasi sangat menyimpang dari perilaku dunia nyata sehingga menjadi masalah (misalnya GET setelah 302 menanggapi POST).

Jika server upstream tahu apa pengkodean yang benar, itu harus memberi sinyal. Jika tidak, kita akan mengikuti apa yang dikatakan spesifikasi. =)

Jika menurut Anda standar spesifikasinya buruk, saya sangat menganjurkan Anda untuk terlibat dengan proses RFC untuk HTTP/2.0 agar default ini diubah. =)

Apa yang @Lukasa katakan + fakta bahwa jika penyandian yang diambil dari header tidak ada, kami mengandalkan sandiwara untuk menebak penyandiannya. Dengan begitu sedikit karakter, sandiwara tidak akan mengembalikan sesuatu yang pasti karena menggunakan data statistik untuk menebak pengkodean yang tepat.

Terus terang, tahun tidak ada bedanya dan spesifikasi juga tidak berubah.

Jika Anda tahu penyandian apa yang Anda harapkan, Anda juga dapat melakukan penguraian sendiri seperti ini:

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

Tidak ada yang salah dengan permintaan sejauh yang saya ketahui dan ini juga bukan bug dalam sandiwara. Karena @Lukasa sepertinya setuju dengan saya, saya tutup ini.

@lavr (/cc @sigmavirus24), bahkan lebih mudah dari itu, Anda cukup menyediakan pengkodean sendiri.

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

Kemudian, lanjutkan secara normal.

@kennethreitz itu mengecewakan. Mengapa kita membuatnya mudah bagi orang-orang? =P

Sangat :)

Sebagian besar untuk situs web Jepang. Mereka semua berbohong tentang pengkodean mereka.

@sigmavirus24
harap dicatat, bahwa utils.get_encoding_from_headers selalu mengembalikan 'ISO-8859-1', dan sandiwara tidak memiliki kesempatan untuk dipanggil.
jadi bug adalah: kami berharap sandiwara digunakan untuk menebak penyandian, tetapi ternyata tidak.

Tambalan di atas memperbaiki bug, tetapi masih mengikuti RFC.
Tolong, pertimbangkan untuk meninjaunya.

@lavr Maaf, kami tidak menjelaskannya dengan jelas. Kami _tidak_ mengharapkan sandiwara dipanggil dalam kasus ini. RFC sangat jelas: jika Anda tidak menentukan rangkaian karakter, dan tipe MIME adalah text/* , penyandian harus dianggap sebagai ISO-8859-1. Itu berarti "jangan menebak". =)

@lavr : cukup atur r.encoding ke None , dan itu akan berfungsi seperti yang Anda harapkan (saya pikir).

Atau lakukan r.encoding = r.apparent_encoding .

Bahkan lebih baik.

Pada r.encoding = None dan r.encoding = r.apparent_encoding kami kehilangan informasi charset server.
Benar-benar mengabaikan header server bukanlah solusi yang baik, saya pikir.

Solusi yang tepat adalah seperti ini:

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

Terlihat aneh :(

Atau lakukan ini:

r = requests.get(...)

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

Saya tidak berpikir begitu :)

Kondisi r.encoding is None tidak masuk akal, karena r.encoding tidak akan pernah menjadi None untuk content-type=text/*.

r.encoding == 'ISO-8859-1' ... apa artinya? Server mengirim charset='ISO-8859-1' atau server tidak mengirim charset? Jika pertama, saya seharusnya tidak menebak charset.

@lavr Saya meliput basis non-teks. Anda dapat mengesampingkan kemungkinan charset dengan menggunakan kondisi ini sebagai gantinya:

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

@Lukasa
Yah, saya bisa menggunakan peretasan ini.
Dan semua orang di Eropa Timur dan Asia dapat menggunakannya.

Tetapi bagaimana jika kita memperbaikinya dalam permintaan? ;)
Bagaimana jika permintaan dapat dengan jujur ​​menetapkan enconding=None pada respons tanpa charset ?

Seperti yang telah kita bahas berkali-kali, Permintaan mengikuti spesifikasi HTTP secara tertulis. Perilaku saat ini tidak salah. =)

Fakta bahwa itu tidak membantu untuk kasus penggunaan Anda adalah cerita lain. =)

Baiklah, cukup sekian pembahasan tentang ini. Terima kasih untuk umpan baliknya.

HTTP 1.1 yang diperbarui menghapus rangkaian karakter default ISO-8859-1: http://tools.ietf.org/html/rfc7231#appendix -B

Kami sudah melacak ini di #2086. =)

Bagi yang berkepentingan, berikut adalah tambalan kompatibilitas

buat file requests_patch.py dengan kode berikut dan impor, maka masalahnya harus diselesaikan.

#!/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), bahkan lebih mudah dari itu, Anda cukup menyediakan pengkodean sendiri.

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

Kemudian, lanjutkan secara normal.

Terima kasih untuk ini! Adakah ide tentang bagaimana melakukannya dalam satu baris?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

eromoe picture eromoe  ·  3Komentar

brainwane picture brainwane  ·  3Komentar

cnicodeme picture cnicodeme  ·  3Komentar

JimHokanson picture JimHokanson  ·  3Komentar

NoahCardoza picture NoahCardoza  ·  4Komentar