scraping - ¿Cómo descargar una página web(!) Con el juego de caracteres correcto en Python?
descargar pagina web con python (7)
en lugar de tratar de obtener una página y luego descifrar el conjunto de caracteres que utilizaría el navegador, ¿por qué no utilizar un navegador para buscar la página y comprobar qué juego de caracteres utiliza?
from win32com.client import DispatchWithEvents
import threading
stopEvent=threading.Event()
class EventHandler(object):
def OnDownloadBegin(self):
pass
def waitUntilReady(ie):
"""
copypasted from
http://mail.python.org/pipermail/python-win32/2004-June/002040.html
"""
if ie.ReadyState!=4:
while 1:
print "waiting"
pythoncom.PumpWaitingMessages()
stopEvent.wait(.2)
if stopEvent.isSet() or ie.ReadyState==4:
stopEvent.clear()
break;
ie = DispatchWithEvents("InternetExplorer.Application", EventHandler)
ie.Visible = 0
ie.Navigate(''http://kskky.info'')
waitUntilReady(ie)
d = ie.Document
print d.CharSet
Problema
Cuando se explora una página web usando python, uno debe conocer la codificación de caracteres de la página. Si obtienes la codificación de caracteres incorrecta, tu salida se arruinará.
La gente generalmente usa alguna técnica rudimentaria para detectar la codificación. O usan el conjunto de caracteres del encabezado o el juego de caracteres definido en la metaetiqueta o usan un detector de codificación (que no se preocupa por metaetiquetas o encabezados). Al usar solo una de estas técnicas, a veces no obtendrás el mismo resultado que obtendrías en un navegador.
Los navegadores lo hacen de esta manera:
- Las metaetiquetas siempre tienen prioridad (o definición xml)
- La codificación definida en el encabezado se usa cuando no hay un juego de caracteres definido en una metaetiqueta
- Si la codificación no está definida en absoluto, es hora de la detección de codificación.
(Bueno ... al menos esa es la forma en que creo que la mayoría de los navegadores lo hacen. La documentación es realmente escasa).
Lo que estoy buscando es una biblioteca que pueda decidir el conjunto de caracteres de una página como lo haría un navegador. Estoy seguro de que no soy el primero que necesita una solución adecuada para este problema.
Solución (aún no lo he probado ...)
De acuerdo con la documentación de Beautiful Soup .
Beautiful Soup prueba las siguientes codificaciones, en orden de prioridad, para convertir su documento en Unicode:
- Una codificación que pasa como el argumento fromEncoding al constructor de sopa.
- Una codificación descubierta en el documento en sí: por ejemplo, en una declaración XML o (para documentos HTML) una etiqueta META de http-equiv. Si Beautiful Soup encuentra este tipo de codificación dentro del documento, vuelve a analizar el documento desde el principio y prueba la nueva codificación. La única excepción es si especificó explícitamente una codificación, y esa codificación realmente funcionó: entonces ignorará cualquier codificación que encuentre en el documento.
- Una codificación olfateó al mirar los primeros bytes del archivo. Si se detecta una codificación en esta etapa, será una de las codificaciones UTF- *, EBCDIC o ASCII.
- Una codificación olfateada por la biblioteca chardet, si la tienes instalada.
- UTF-8
- Windows-1252
Cuando descargue un archivo con urllib o urllib2, puede averiguar si se transmitió un encabezado de conjunto de caracteres:
fp = urllib2.urlopen(request)
charset = fp.headers.getparam(''charset'')
Puede usar BeautifulSoup para ubicar un metaelemento en el HTML:
soup = BeatifulSoup.BeautifulSoup(data)
meta = soup.findAll(''meta'', {''http-equiv'':lambda v:v.lower()==''content-type''})
Si ninguno de los dos está disponible, los navegadores generalmente vuelven a la configuración del usuario, combinado con la detección automática. Como Rajax propone, puedes usar el módulo chardet. Si tiene la configuración de usuario disponible diciéndole que la página debe ser china (por ejemplo), es posible que pueda hacerlo mejor.
Parece que necesitas un híbrido de las respuestas presentadas:
- Obtener la página usando urllib
- Encuentra etiquetas
<meta>
usando una hermosa sopa u otro método - Si no existen metaetiquetas, compruebe los encabezados devueltos por urllib
- Si eso todavía no le da una respuesta, use el detector de codificación universal.
Honestamente, no creo que vayas a encontrar nada mejor que eso.
De hecho, si sigue leyendo las preguntas frecuentes a las que se vinculó en los comentarios de la otra respuesta, eso es lo que defiende el autor de la biblioteca detectora.
Si cree en las preguntas frecuentes, esto es lo que hacen los navegadores (tal como lo solicitó en su pregunta original) ya que el detector es un puerto del código de detección de Firefox.
Use el detector de codificación universal :
>>> import chardet
>>> chardet.detect(urlread("http://google.cn/"))
{''encoding'': ''GB2312'', ''confidence'': 0.99}
La otra opción sería simplemente usar wget:
import os
h = os.popen(''wget -q -O foo1.txt http://foo.html'')
h.close()
s = open(''foo1.txt'').read()
BeautifulSoup dosifica esto con UnicodeDammit: Unicode, Dammit
Yo usaría html5lib para esto.
Scrapy descarga una página y detecta una codificación correcta para ella, a diferencia de requests.get (url) .text o urlopen. Para hacerlo, intenta seguir las reglas del navegador: esto es lo mejor que se puede hacer, porque los propietarios de sitios web tienen incentivos para hacer que sus sitios web funcionen en un navegador. La depuración necesita tomar encabezados HTTP, etiquetas <meta>
, marcas de lista de materiales y diferencias en los nombres de codificación en la cuenta.
La adivinación basada en contenido (chardet, UnicodeDammit) por sí sola no es una solución correcta, ya que puede fallar; solo debe utilizarse como último recurso cuando los encabezados o <meta>
o las marcas de la lista de materiales no estén disponibles o no proporcionen información.
No tiene que usar Scrapy para obtener sus funciones de detección de codificación; se lanzan (entre otras cosas) en una biblioteca separada llamada w3lib: https://github.com/scrapy/w3lib .
Para obtener la codificación de página y el cuerpo de unicode, use la función w3lib.encoding.html_to_unicode , con un respaldo de adivinación basado en el contenido:
import chardet
from w3lib.encoding import html_to_unicode
def _guess_encoding(data):
return chardet.detect(data).get(''encoding'')
detected_encoding, html_content_unicode = html_to_unicode(
content_type_header,
html_content_bytes,
default_encoding=''utf8'',
auto_detect_fun=_guess_encoding,
)