2010-12-22 8 views
12

Estoy un poco sorprendido de que sea tan complicado obtener un juego de caracteres de una página web con Python. ¿Me estoy perdiendo una manera? HTTPMessage tiene muchas funciones, pero no esto.¿Cuál es una manera breve buena y confiable de obtener el juego de caracteres de una página web?

>>> google = urllib2.urlopen('http://www.google.com/') 
>>> google.headers.gettype() 
'text/html' 
>>> google.headers.getencoding() 
'7bit' 
>>> google.headers.getcharset() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: HTTPMessage instance has no attribute 'getcharset' 

Así que tienes que obtener el encabezado y dividirlo. Dos veces.

>>> google = urllib2.urlopen('http://www.google.com/') 
>>> charset = 'ISO-8859-1' 
>>> contenttype = google.headers.getheader('Content-Type', '') 
>>> if ';' in contenttype: 
...  charset = contenttype.split(';')[1].split('=')[1] 
>>> charset 
'ISO-8859-1' 

Esa es una cantidad sorprendente de pasos para una función tan básica. ¿Me estoy perdiendo de algo?

+2

De RFC 2616 (HTTP1.1) 'El El parámetro "charset" se usa con algunos tipos de medios para definir el juego de caracteres (sección 3.4) de los datos. Cuando el remitente no proporciona ningún parámetro de juego de caracteres explícito, los subtipos de medios del tipo "texto" se definen para tener un valor de juego de caracteres predeterminado de "ISO-8859-1" cuando se recibe a través de HTTP., Como una nota al margen por defecto siendo ASCII – plundra

+0

@plundra: Bueno, ISO-8859-1 es un superconjunto de ASCII, pero estás en lo cierto: es una codificación diferente. – Piskvor

+0

@Piskvor: Y si uno fuera a usar el 'charset' de arriba con s.decode() por ejemplo, las cosas se romperán (con las páginas enviando iso-8859-1 y confiando en implícito) – plundra

Respuesta

0

Usted no está perdiendo nada. Está haciendo lo correcto: la codificación de una respuesta HTTP es una subparte de Content-Type.

Tenga en cuenta también que algunas páginas pueden enviar solo Content-Type: text/html y luego establecer la codificación a través de <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - eso es un hack feo (por parte del autor de la página) y no es demasiado común.

0

Me gustaría ir con chardet Universal Encoding Detector.

>>> import urllib 
>>> urlread = lambda url: urllib.urlopen(url).read() 
>>> import chardet 
>>> chardet.detect(urlread("http://google.cn/")) 
{'encoding': 'GB2312', 'confidence': 0.99} 

que está haciendo bien, pero su enfoque podría fallar por páginas en las que se declara conjunto de caracteres en meta etiqueta o no se declara en absoluto.
Si miras más de cerca las fuentes de Chardet, tiene un módulo charsetprober/charsetgroupprober que trata muy bien este problema.

+0

Para mí, esta no es una buena respuesta: chardet es "adivinar la codificación del archivo [HTML]" (ver https://github.com/erikrose/chardet). Debería, por supuesto, primero comenzar buscando en los encabezados si se declara. Vea la pregunta apuntada por Leniel. – lajarre

3

hice algunas investigaciones y se acercó con esta solución:

response = urllib.request.urlopen(url) 
encoding = response.headers.get_content_charset() 

Ésta es la forma en que lo haría en Python 3. No he probado en Python 2 pero estoy adivinando que tendría para usar urllib2.request en lugar de urllib.request.

Así es como funciona, ya que la documentación oficial de Python no lo explica muy bien: el resultado de urlopen es un objeto http.client.HTTPResponse. La propiedad headers de este objeto es un objeto http.client.HTTPMessage que, según la documentación, "se implementa utilizando la clase email.message.Message", que tiene un método llamado get_content_charset, que intenta determinar y devolver el juego de caracteres de la respuesta.

Por defecto, este método devuelve None si no es capaz de determinar el conjunto de caracteres, pero se puede anular este comportamiento no por pasar un parámetro failobj:

encoding = response.headers.get_content_charset(failobj="utf-8") 
+0

'get_content_charset' no está disponible en Python 2. Debería poder usar' headers.getparam ("charset") 'en su lugar (solo Python 2; Python 3 lo cambiará a' get_param'). –

Cuestiones relacionadas