Как исправить неправильно декодированную строку UTF-8?

Я использую данные из RESTful API, он возвращает мне строки и целочисленные значения. Однако кажется, что он возвращает некоторые строковые значения, неправильно закодированные/декодированные (вероятно).

Ожидаемая строка:

criança

Получена строка:

criança

Вот мой код:

url = "https://analytics.us.algolia.com/2/searches?index = {index}&startDate = {yesterday}".format(index=index, yesterday=yesterday)
headers = { 'X-Algolia-Application-Id': app_id,
            'X-Algolia-API-Key': app_key,
            'Content-Type': 'application/json; charset=utf-8'}

response = requests.get(url, headers=headers)
response_json = json.loads(response.text)

print(response_json)

Это для сценария Python 3.6.x, который будет получать данные из RESTful API Algolia и сохранять их в Amazon Redshift. Я пишу этот скрипт на Ubuntu 18.04, мой набор кодировок символов терминала — pt_BR.UTF-8 (echo $LANG) и UTF-8 (locale charmap).

Я вижу, что полученные данные неверны, когда я печатаю их перед сохранением в базе данных, которая настроена на использование charset=utf8. Я также могу видеть эти неправильные данные в базе данных через оператор SELECT.

Я нашел этот Таблица отладки кодировки UTF-8, он указывает, что, вероятно, это произошло из-за того, что байты UTF-8 интерпретируются как байты Windows-1252 (или ISO 8859-1).

Как я могу лечить это с помощью некоторой функции/библиотеки Python?

Это означает, что ответ был обработан как ASCII вместо UTF8. Дело не в кодировке. Страница, которую вы читаете прямо сейчас, имеет кодировку UTF8, и тем не менее, если вы проверите источник, вы увидите, что кодировка не используется. Пожалуйста, опубликуйте свой код. Скорее всего, там есть вызов, который пытается «декодировать» или преобразовать эту строку во что-то другое, поэтому проблема вызывая

Panagiotis Kanavos 21.06.2019 15:59

Другая возможность состоит в том, что строка верна, но то, как вы чек об оплате, это не так. Если ваш терминал или консоль не настроен на UTF8, байты UTF8 будут отображаться так, как если бы они были символами ASCII, а два байта, представляющие ç, будут отображаться как ç. Если вы сохранили данные в файл, возможно, вы читаете их как ASCII вместо UTF8.

Panagiotis Kanavos 21.06.2019 16:02

@PanagiotisKanavos, спасибо за вашу поддержку. Я проверил свою кодировку символов терминала, она установлена ​​​​на UTF-8. Есть ли какие-либо настройки, которые я должен сделать явно, чтобы Python работал с UTF-8?

Gabriel Ataide 21.06.2019 16:36

Ничего. Ничего не нужно. Разместите свой код. Каким-то образом Это обрабатывает ответ как ASCII вместо UTF8. Или может случиться так, что ответ имеет неправильный charset в заголовке Content-Type. Что происходит, когда вы пытаетесь получить страницу это?

Panagiotis Kanavos 21.06.2019 16:48

@PanagiotisKanavos технически не печатает ASCII, потому что эти символы находятся за пределами диапазона ASCII. В Python 3.6 все строки должны автоматически декодироваться в Unicode на входе и преобразовываться в соответствующую кодировку на выходе. Я согласен, что нам действительно нужно увидеть код, чтобы увидеть, что происходит.

Mark Ransom 21.06.2019 16:52

Я только что отредактировал пост, вот код, ребята.

Gabriel Ataide 21.06.2019 17:33

Что такое заголовок ответа Content-Type? Каковы байты для criança в теле ответа?

Tom Blodget 24.06.2019 00:15

@MarkRansom выбери свое имя для single byte codepage. Независимо от того, что вы выберете, ASCII или ANSI, кто-то всегда скажет, что все наоборот. В итоге я использовал US-ASCII для 7-битной кодовой страницы, ASCII для всего остального и этот комментарий, когда это необходимо.

Panagiotis Kanavos 24.06.2019 09:23

@GabrielAtaide Content-Type разрешено только в запросах PUT/POST. В GET он может отображаться только как заголовок отклик. Используйте Accept-Charset, чтобы запросить ответ UTF8. Я подозреваю, что служба HTTP, которую вы называете, по умолчанию использует Latin1 (он же 1252, ISO-8859-1)

Panagiotis Kanavos 24.06.2019 09:46

@GabrielAtaide, несмотря на это, ответ является UTF8 и терминал должен отображают его правильно. Скорее всего проблема в неправильно настроенной среде. Попробуйте установить LC_ALL на pt_BR.UTF-8 или даже en_US.UTF-8

Panagiotis Kanavos 24.06.2019 10:05
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
10
4 244
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Библиотека requests пытается выполнить угадать кодировку ответа. Возможно, requests расшифровывает ответ как cp1252 (он же Windows-1252).

Я предполагаю, что если вы возьмете этот текст и закодируете его обратно в cp1252, а затем декодируете как utf-8, вы увидите правильный текст:

>>> 'criança'.encode('cp1252').decode('utf-8')
'criança'

Исходя из этого, я предполагаю, что если вы спросите свой объект ответа, какую кодировку он угадал, он скажет вам cp1252:

>>> response.encoding
'cp1252'

Принуждение requests к декодированию как utf-8, как это, вероятно, решит вашу проблему:

>>> response.encoding = 'utf-8'

В Windows нет кодировки по умолчанию, если вы не имеете в виду UTF16. Строки в Windows имеют кодировку UTF16. Локаль пользователя и системы влияет на то, как обрабатывается текст *не*Unicode. В любом случае ОП упомянул, что используется Ubuntu, и переменная среды $LANG (pt_BR.UTF-8) не должна вызывать проблем.

Panagiotis Kanavos 24.06.2019 09:28

@PanagiotisKanavos Я написал это неясно. Я не имел в виду, что кодировка операционной системы имеет значение. Библиотека запросов использует некоторую эвристику для угадывания кодировок (я только что добавил ссылку на документацию об этом). Возможно, он угадал кодировку Windows-1252, вероятно, на основе неправильного заголовка HTTP.

Trey Hunner 24.06.2019 15:34

@TreyHunner, спасибо! Это сработало! Тем не менее, когда я получил правильную строку типа criança, возникло исключение: UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe7 in position 5: invalid continuation byte. Таким образом, мне пришлось добавить try, чтобы использовать ваше решение, и, для сценария получения правильной строки, except, содержащий: text = text.encode('cp1252').decode('latin-1').encode('utf-8').deco‌​de('utf-8'). Таким образом, я могу в любом случае обработать ответ как utf-8.

Gabriel Ataide 26.06.2019 20:00

@GabrielAtaide, похоже, вы иногда получаете байты, которые на самом деле правильно закодированы в utf-8. В этом случае это кодирование как cp1252, а затем декодирование как utf-8 приведет к ошибке, подобной той, которую вы связали. Вы можете прочитать response.content и использовать библиотеку шарде, чтобы угадать кодировку для вас, а затем вручную декодировать байты. Таким образом, вы можете избежать повторного кодирования неправильно угаданных байтов и надеяться на лучшее.

Trey Hunner 27.06.2019 01:37

если проблема не устранена, скопируйте проект в другую папку, заново импортируйте проект с другим именем файла проекта. Сначала перезапустите Android Studio, а затем импортируйте проект из другой папки, и проблема должна быть устранена!

Другие вопросы по теме