La mejor forma de descodificar la encoding Unicoding desconocida en Python 2.5

¿Lo tengo todo bien? De todos modos, estoy analizando un montón de html, pero no siempre sé qué encoding tiene que ser (un sorprendente número de mentira). El siguiente código muestra fácilmente lo que he estado haciendo hasta ahora, pero estoy seguro de que hay una mejor manera. Tus sugerencias serían muy apreciadas.

import logging import codecs from utils.error import Error class UnicodingError(Error): pass # these encodings should be in most likely order to save time encodings = [ "ascii", "utf_8", "big5", "big5hkscs", "cp037", "cp424", "cp437", "cp500", "cp737", "cp775", "cp850", "cp852", "cp855", "cp856", "cp857", "cp860", "cp861", "cp862", "cp863", "cp864", "cp865", "cp866", "cp869", "cp874", "cp875", "cp932", "cp949", "cp950", "cp1006", "cp1026", "cp1140", "cp1250", "cp1251", "cp1252", "cp1253", "cp1254", "cp1255", "cp1256", "cp1257", "cp1258", "euc_jp", "euc_jis_2004", "euc_jisx0213", "euc_kr", "gb2312", "gbk", "gb18030", "hz", "iso2022_jp", "iso2022_jp_1", "iso2022_jp_2", "iso2022_jp_2004", "iso2022_jp_3", "iso2022_jp_ext", "iso2022_kr", "latin_1", "iso8859_2", "iso8859_3", "iso8859_4", "iso8859_5", "iso8859_6", "iso8859_7", "iso8859_8", "iso8859_9", "iso8859_10", "iso8859_13", "iso8859_14", "iso8859_15", "johab", "koi8_r", "koi8_u", "mac_cyrillic", "mac_greek", "mac_iceland", "mac_latin2", "mac_roman", "mac_turkish", "ptcp154", "shift_jis", "shift_jis_2004", "shift_jisx0213", "utf_32", "utf_32_be", "utf_32_le", "utf_16", "utf_16_be", "utf_16_le", "utf_7", "utf_8_sig" ] def unicode(string): '''make unicode''' for enc in self.encodings: try: logging.debug("unicoder is trying " + enc + " encoding") utf8 = unicode(string, enc) logging.info("unicoder is using " + enc + " encoding") return utf8 except UnicodingError: if enc == self.encodings[-1]: raise UnicodingError("still don't recognise encoding after trying do guess.") 

Hay dos bibliotecas de propósito general para detectar codificaciones desconocidas:

  • Chardet, parte de Universal Feed Parser
  • UnicodeDammit, parte de Beautiful Soup

Chardet se supone que es un puerto de la forma en que Firefox lo hace

Puede usar la siguiente expresión regular para detectar utf8 a partir de cadenas de bytes:

 import re utf8_detector = re.compile(r"""^(?: [\x09\x0A\x0D\x20-\x7E] # ASCII | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 )*$""", re.X) 

En la práctica, si estás tratando con inglés, he encontrado los siguientes trabajos el 99.9% del tiempo:

  1. si pasa la expresión regular anterior, es ascii o utf8
  2. si contiene bytes de 0x80-0x9f pero no 0xa4, es Windows-1252
  3. si contiene 0xa4, asume que es latino-15
  4. de lo contrario, asume que es latino-1

He abordado el mismo problema y descubrí que no hay forma de determinar el tipo de encoding de un contenido sin metadatos sobre el contenido. Es por eso que terminé con el mismo enfoque que intentas aquí.

Mi único consejo adicional a lo que ha hecho es que, en lugar de ordenar la lista de posibles codificaciones en el orden más probable, debe ordenarlas por especificidad. Descubrí que ciertos conjuntos de caracteres son subconjuntos de otros, por lo que si utf_8 como su segunda opción, extrañará encontrar los subconjuntos de utf_8 (creo que uno de los conjuntos de caracteres coreanos usa el mismo espacio numérico que utf ).

Ya que estás usando Python, puedes probar UnicodeDammit . Es parte de Beautiful Soup que también te puede resultar útil.

Como sugiere su nombre, UnicodeDammit intentará hacer lo que sea necesario para obtener un Unicode adecuado de la basura que pueda encontrar en el mundo.