Перевод многобайтовых символов в 7-битный ASCII в Python

Я загружаю и анализирую веб-страницу через скрипт Python. Мне нужно, чтобы он был закодирован в 7-битный ASCII для дальнейшей обработки. Я использую библиотеку запросов (http://docs.python-requests.org/en/master/) в virtualenv, основанном на том, что есть в Ubuntu 16.04 LTS.

Я бы хотел, чтобы пакет запросов или какой-то другой пакет обрабатывал перевод в ASCII, не требуя от меня дальнейшего перевода закодированных символов, потому что я знаю, что пропущу некоторые символы. Детали следующие:

Мой текущий скрипт Python, показанный ниже, использует кодировку ISO-8859-1 в попытке принудительно преобразовать данные результата в 7-битный ASCII с некоторым частичным успехом. Но я установил кодировку результатаа также также закодировать текст, когда он выходит. Это кажется странным и, на самом деле, совершенно неправильным. Но даже если я живу с этим, у меня есть главная проблема, которая заключается в следующем:

Даже после кодирования, я вижу штрихи, закодированные в том, что, кажется, находится в некотором наборе символов не ASCII. Как будто символы тире проскальзывали через кодировку запросов. Приведенный ниже сценарий решает эту проблему путем поиска и замены многобайтовой кодировки тире на символ ASCII. Это не имеет большого значения, если это один многобайтовый символ, но подозреваю, что есть другие символы, которые нужно будет перевести на другие веб-страницы, которые я хочу обработать. Нужно ли просто использовать какую-либо другую кодировку, отличную от «ISO-8859-1», с объектом запросов?

Вот мой скрипт (с использованием Python 2.7.11 на Ubuntu 16.04 LTS на 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)

Вызов simple_wget.py в рамках virtualenv показывает:

[email protected]:~$ simple_wget.py
line:                           <td>2.6 up to 3.5 GHz – 6 MB cache – 4 cores – 8 threads</td>
fixed_line                      <td>2.6 up to 3.5 GHz - 6 MB cache - 4 cores - 8 threads</td>

Передача этого выхода черезoc -cb чтобы увидеть восьмеричные значения ("342 200 223") символов тире, соответствующихr'\xe2\x80\x93' в сценарии выше:

[email protected]:~$ simple_wget.py | od -cb
0000000   l   i   n   e   :                          \t  \t  \t  \t  \t
        154 151 156 145 072 040 040 040 040 040 040 011 011 011 011 011
0000020  \t   <   t   d   >   2   .   6       u   p       t   o       3
        011 074 164 144 076 062 056 066 040 165 160 040 164 157 040 063
0000040   .   5       G   H   z     342 200 223       6       M   B    
        056 065 040 107 110 172 040 342 200 223 040 066 040 115 102 040
0000060   c   a   c   h   e     342 200 223       4       c   o   r   e
        143 141 143 150 145 040 342 200 223 040 064 040 143 157 162 145
0000100   s     342 200 223       8       t   h   r   e   a   d   s   <
        163 040 342 200 223 040 070 040 164 150 162 145 141 144 163 074
0000120   /   t   d   >  \n   f   i   x   e   d   _   l   i   n   e    
        057 164 144 076 012 146 151 170 145 144 137 154 151 156 145 040
0000140  \t  \t  \t  \t  \t  \t   <   t   d   >   2   .   6       u   p
        011 011 011 011 011 011 074 164 144 076 062 056 066 040 165 160
0000160       t   o       3   .   5       G   H   z       -       6    
        040 164 157 040 063 056 065 040 107 110 172 040 055 040 066 040
0000200   M   B       c   a   c   h   e       -       4       c   o   r
        115 102 040 143 141 143 150 145 040 055 040 064 040 143 157 162
0000220   e   s       -       8       t   h   r   e   a   d   s   <   /
        145 163 040 055 040 070 040 164 150 162 145 141 144 163 074 057
0000240   t   d   >  \n
        164 144 076 012
0000244
[email protected]:~$

Вещи, которые я пробовал:

https://stackoverflow.com/a/19645137/257924 подразумевает использование кодировкиascii, но он задыхается внутри библиотеки запросов. Меняем скрипт на:

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

выходы:

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

Изменение последней строки выше, чтобы быть

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

в результате черточки просто удаляются, а не переводятся, а это не то, что я хочу.

И это тоже не работает вообще:

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)

потому что это дает эту ошибку:

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

что, насколько я могу понять изhttps://stackoverflow.com/a/10385520/257924, из-за того, что «данные» не являются строкой Unicode. Таблица перевода из 256 символов не будет делать то, что мне нужно. И кроме того, это излишне: что-то внутри Python должно переводить эти многобайтовые символы, не требуя взлома кода на моем уровне сценария.

Кстати, меня не интересует многоязычный перевод страниц. Ожидается, что все переведенные страницы будут на американском или британском английском.

 roeland04 июл. 2016 г., 04:54
Американский или британский английский могут содержать символы, отличные от ascii, такие как тире -, кавычки «», апострофы и другие © ® ... Может быть, вы найдете некоторые смайлики ☹️. Некоторые тексты используют † для сносок. Если вы вообще можете этого избежать, не пытайтесь сократить текст до ASCII, даже если он английский.
 bgoodr07 июл. 2016 г., 17:29
Я могу быть вынужден избегать этого, как вы сказали. Теперь, когдаstackoverflow.com/a/38178064/257924 поставил меня прямо на правильное использование, я буду знать, что мне нужно просить в следующем, отдельном вопросе.

Ответы на вопрос(1)

Решение Вопроса

что вам нужно для чистой обработки не ASCII-символов ... при условии, что вы объявите правильную кодировку. Ваш входной файл в кодировке UTF8, а не ISO-8859-1, потому чтоr'\xe2\x80\x93' является кодировкой UTF8 для символа EN DASH или UnicodeU+2013.

Итак, вы должны:

загрузить текст из запроса в виде истинной строки 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

переведитеоскорбительный персонажив юникоде:

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

Это будет работать сейчас, потому чтоtranslate Карта отличается для байтовых и юникодных строк. Для байтовых строк таблица перевода должна быть строкой длиной 256, тогда как для строк Unicode она должна быть отображением ординалов Unicode в ординалы Unicode, строки Unicode или None (ref: Справочное руководство по стандартной библиотеке Python).

тогда вы можете безопасно кодировать данные в байтовую строку ascii:

tdata = data.encode('ascii')

Приведенная выше команда выдаст исключение, если в переводчике останутся не переведенные не ascii символыdata Строка Юникода. Вы можете видеть это как помощь, чтобы быть уверенным, что все так же успешно преобразовано.

 bgoodr07 июл. 2016 г., 17:31
Это было довольно полезно. Я понимаю, что мне нужен новый вопрос, чтобы уточнить мое основное намерение - полностью избежать жесткого кодирования charmap в моем коде с помощью стандартных или предоставляемых PyPy библиотек python для предоставления этого charmap из коробки. Таким образом, отметив это как ответ.
 bgoodr07 июл. 2016 г., 17:38
Сообщениеstackoverflow.com/questions/38249708/... как новый отдельный вопрос

Ваш ответ на вопрос