Traducción de caracteres de múltiples bytes en ASCII de 7 bits en Python

Estoy descargando y analizando una página web a través de un script de Python. Necesito que esté codificado en ASCII de 7 bits para su posterior procesamiento. Estoy utilizando la biblioteca de solicitudes ( http://docs.python-requests.org/en/master/ ) en un virtualenv basado en lo que sea que tenga Ubuntu 16.04 LTS.

Me gustaría que el paquete de solicitudes, o algún paquete, se encargue de la traducción a ASCII, sin necesidad de que haga más traducción de los caracteres codificados, porque sé que voy a extrañar algunos caracteres. Los detalles son los siguientes:

Mi script de Python actual, que se muestra a continuación, utiliza una encoding de ISO-8859-1 en un bash de forzar la conversión de los datos de resultados a ASCII de 7 bits, con cierto éxito parcial. Pero, he establecido la encoding del resultado y también codifico el texto cuando sale. Eso parece extraño, y de hecho, francamente equivocado. Pero incluso si vivo con eso, tengo el problema principal que es el siguiente:

Incluso después de la encoding, veo guiones codificados en lo que parece ser en un conjunto de caracteres no ASCII. Es como si los caracteres del guión se deslizaran a través de la encoding de las solicitudes. La siguiente secuencia de comandos trata sobre esto buscando y reemplazando la encoding de guiones de múltiples bytes con un carácter de guión ASCII. Esto no es un gran problema si se trata de un carácter de varios bytes, pero sospeche que hay otros caracteres que deberán ser traducidos en otras páginas web que deseo procesar. ¿Simplemente necesito usar otra encoding que no sea ‘ISO-8859-1’ con el objeto de solicitudes?

Aquí está mi script (usando Python 2.7.11 en Ubuntu 16.04 LTS en x86_64):

    #!/bin/bash import sys import os import string import re import requests url = "https://system76.com/laptops/kudu" r = requests.get(url) # # Why do I have to BOTH set r.encoding AND call r.text.encode # in order to avoid the errors?: # encoding = 'ISO-8859-1' r.encoding = encoding data = r.text.encode(encoding) # # Split the lines out, find the offending line, # and translate the multi-byte characters: # lines = data.splitlines() for line in lines: m = re.search(r'2.6 up to 3.5 GHz', line) if m: print "line: {}".format(line) m = re.search(r'\xe2\x80\x93', line) # The '-' in the next line is a ASCII dash character: fixed_line = re.sub(r'\xe2\x80\x93', '-', line) print "fixed_line {}".format(line) 

    Al invocar simple_wget.py dentro de los progtwigs virtualesenv:

     theuser@thesystem:~$ simple_wget.py line: 2.6 up to 3.5 GHz – 6 MB cache – 4 cores – 8 threads fixed_line 2.6 up to 3.5 GHz - 6 MB cache - 4 cores - 8 threads 

    Al pasar esa salida a través de oc -cb para ver los valores octales (“342 200 223”) de los caracteres de guión correspondientes a la r'\xe2\x80\x93' en el script anterior:

     theuser@thesystem:~$ simple_wget.py | od -cb 0000000 line : \t \t \t \t \t 154 151 156 145 072 040 040 040 040 040 040 011 011 011 011 011 0000020 \t  2 . 6 upto 3 011 074 164 144 076 062 056 066 040 165 160 040 164 157 040 063 0000040 . 5 GH z 342 200 223 6 MB 056 065 040 107 110 172 040 342 200 223 040 066 040 115 102 040 0000060 cache 342 200 223 4 core 143 141 143 150 145 040 342 200 223 040 064 040 143 157 162 145 0000100 s 342 200 223 8 threads  \nfixed _ line 057 164 144 076 012 146 151 170 145 144 137 154 151 156 145 040 0000140 \t \t \t \t \t \t  2 . 6 up 011 011 011 011 011 011 074 164 144 076 062 056 066 040 165 160 0000160 to 3 . 5 GH z - 6 040 164 157 040 063 056 065 040 107 110 172 040 055 040 066 040 0000200 MB cache - 4 cor 115 102 040 143 141 143 150 145 040 055 040 064 040 143 157 162 0000220 es - 8 threads  \n 164 144 076 012 0000244 theuser@thesystem:~$ 

    Cosas que he probado:

    https://stackoverflow.com/a/19645137/257924 implica el uso de una encoding de ascii , pero se ahoga dentro de la biblioteca de solicitudes. Cambiando el script para que sea:

     #encoding = 'ISO-8859-1' encoding = 'ascii' # try https://stackoverflow.com/a/19645137/257924 r.encoding = encoding data = r.text.encode(encoding) 

    rendimientos

     theuser@thesystem:~$ ./simple_wget Traceback (most recent call last): File "./simple_wget.py", line 18, in  data = r.text.encode(encoding) UnicodeEncodeError: 'ascii' codec can't encode characters in position 10166-10168: ordinal not in range(128) 

    Cambiando la última línea de arriba para que sea

     data = r.text.encode(encoding, "ignore") 

    los resultados en los guiones que solo se eliminan, no se traducen, lo que no es lo que quiero.

    Y esto tampoco funciona en absoluto:

     encoding = 'ISO-8859-1' r.encoding = encoding data = r.text.encode(encoding) charmap = { 0x2014: u'-', # em dash 0x201D: u'"', # comma quotation mark, double # etc. } data = data.translate(charmap) 

    porque da este error:

     Traceback (most recent call last): File "./simple_wget.py", line 30, in  data = tmp2.translate(charmap) TypeError: expected a string or other character buffer object 

    que es, por lo que puedo entender de https://stackoverflow.com/a/10385520/257924 , debido a que los “datos” no son una cadena Unicode. Una tabla de traducción de 256 caracteres no va a hacer lo que necesito de todos modos. Y además eso es una exageración: algo dentro de Python debería traducir estos caracteres de múltiples bytes sin requerir código de hackeo en mi nivel de script.

    Por cierto, no estoy interesado en la traducción de páginas en varios idiomas. Se espera que todas las páginas traducidas estén en inglés estadounidense o británico.

    Python tiene todo lo que necesita para procesar de forma limpia los caracteres que no son ASCII … siempre que declare la encoding correcta. Su archivo de entrada tiene encoding UTF8, no ISO-8859-1, porque r'\xe2\x80\x93' es la encoding UTF8 para el carácter EN DASH o Unicode U+2013 .

    Así que deberías:

    • carga el texto de la solicitud como una verdadera cadena de Unicode:

       url = "https://system76.com/laptops/kudu" r = requests.get(url) r.encoding = "UTF-8" data = r.text # ok, data is a true unicode string 
    • traduce caracteres ofensivos en unicode :

       charmap = { 0x2014: u'-', # em dash 0x201D: u'"', # comma quotation mark, double # etc. } data = data.translate(charmap) 

      Funcionará ahora, porque el mapa de translate es diferente para las cadenas de bytes y Unicode. Para las cadenas de bytes, la tabla de traducción debe ser una cadena de longitud 256, mientras que para las cadenas Unicode debe ser una asignación de los ordinales de Unicode a los ordinales de Unicode, las cadenas de Unicode o Ninguno ( ref: Manual de referencia de la biblioteca estándar de Python ).

    • entonces puede codificar de forma segura los datos en una cadena de bytes ASCII:

       tdata = data.encode('ascii') 

      El comando anterior emitirá una excepción si algunos caracteres no traducidos no ascii permanecen en la cadena de data unicode. Puede verlo como una ayuda para asegurarse de que todo se haya convertido correctamente.