Actualización 2018:
A partir de febrero de 2018, el uso de compresiones se gzip
ha vuelto bastante popular (alrededor del 73% de todos los sitios web lo usan, incluidos sitios grandes como Google, YouTube, Yahoo, Wikipedia, Reddit, Stack Overflow y Stack Exchange Network).
Si realiza una decodificación simple como en la respuesta original con una respuesta comprimida, obtendrá un error similar o similar a este:
UnicodeDecodeError: el códec 'utf8' no puede decodificar el byte 0x8b en la posición 1: byte de código inesperado
Para decodificar una respuesta gzpipped, debe agregar los siguientes módulos (en Python 3):
import gzip
import io
Nota: En Python 2 usarías en StringIO
lugar deio
Luego puede analizar el contenido de esta manera:
response = urlopen("https://example.com/gzipped-ressource")
buffer = io.BytesIO(response.read()) # Use StringIO.StringIO(response.read()) in Python 2
gzipped_file = gzip.GzipFile(fileobj=buffer)
decoded = gzipped_file.read()
content = decoded.decode("utf-8") # Replace utf-8 with the source encoding of your requested resource
Este código lee la respuesta y coloca los bytes en un búfer. El gzip
módulo luego lee el búfer utilizando la GZipFile
función. Después de eso, el archivo comprimido puede leerse nuevamente en bytes y descodificarse al texto normalmente legible al final.
Respuesta original de 2010:
¿Podemos obtener el valor real utilizado link
?
Además, generalmente encontramos este problema aquí cuando intentamos .encode()
una cadena de bytes ya codificada. Entonces, puede intentar decodificarlo primero como en
html = urllib.urlopen(link).read()
unicode_str = html.decode(<source encoding>)
encoded_str = unicode_str.encode("utf8")
Como ejemplo:
html = '\xa0'
encoded_str = html.encode("utf8")
Falla con
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 0: ordinal not in range(128)
Mientras:
html = '\xa0'
decoded_str = html.decode("windows-1252")
encoded_str = decoded_str.encode("utf8")
Tiene éxito sin error. Tenga en cuenta que "windows-1252" es algo que utilicé como ejemplo . ¡ Obtuve esto de chardet y tenía 0.5 confianza de que es correcto! (bueno, como se da con una cadena de 1 carácter, qué espera) Debe cambiar eso a la codificación de la cadena de bytes devuelta de .urlopen().read()
lo que se aplica al contenido que recuperó.
Otro problema que veo allí es que el .encode()
método de cadena devuelve la cadena modificada y no modifica la fuente en su lugar. Por lo tanto, es inútil tenerlo, self.response.out.write(html)
ya que html no es la cadena codificada de html.encode (si eso es lo que originalmente buscabas).
Como sugirió Ignacio, revise la página web de origen para la codificación real de la cadena devuelta read()
. Está en una de las etiquetas Meta o en el encabezado ContentType en la respuesta. Use eso entonces como parámetro para .decode()
.
Sin embargo, tenga en cuenta que no debe suponerse que otros desarrolladores son lo suficientemente responsables como para asegurarse de que las declaraciones del encabezado y / o del conjunto de caracteres meta coincidan con el contenido real. (¿Qué es un PITA, sí, debería saber, que era uno de los de antes).