Как правильно разобрать HTML в кодировке UTF-8 в строки Unicode с помощью BeautifulSoup?

Я запускаю программу Python, которая извлекает веб-страницу с кодировкой UTF-8, и я извлекаю текст из HTML с помощью BeautifulSoup.

Однако, когда я пишу этот текст в файл (или печатаю его на консоли), он записывается в неожиданную кодировку.

Пример программы:

import urllib2
from BeautifulSoup import BeautifulSoup

# Fetch URL
url = 'http://www.voxnow.de/'
request = urllib2.Request(url)
request.add_header('Accept-Encoding', 'utf-8')

# Response has UTF-8 charset header,
# and HTML body which is UTF-8 encoded
response = urllib2.urlopen(request)

# Parse with BeautifulSoup
soup = BeautifulSoup(response)

# Print title attribute of a <div> which uses umlauts (e.g. können)
print repr(soup.find('div', id='navbutton_account')['title'])

Выполнение этого дает результат:

# u'Hier k\u0102\u015bnnen Sie sich kostenlos registrieren und / oder einloggen!'

Но я ожидал бы, что строка Unicode Python будет отображать ö в слове können как \xf6:

# u'Hier k\xf6bnnen Sie sich kostenlos registrieren und / oder einloggen!'

Я попытался передать параметр fromEncoding в BeautifulSoup и попытался read() и decode() объект response, но он не имеет никакого значения или вызывает ошибку.

С помощью команды curl www.voxnow.de | hexdump -C я вижу, что веб-страница действительно закодирована в кодировке UTF-8 (т.е. содержит 0xc3 0xb6) для символа ö:

      20 74 69 74 6c 65 3d 22  48 69 65 72 20 6b c3 b6  | title="Hier k..|
      6e 6e 65 6e 20 53 69 65  20 73 69 63 68 20 6b 6f  |nnen Sie sich ko|
      73 74 65 6e 6c 6f 73 20  72 65 67 69 73 74 72 69  |stenlos registri|

Я не могу ограничивать свои возможности Python, поэтому я не понимаю, как отлаживать это дальше. Любые советы?

Ответы

Ответ 1

Как указано выше, мой вопрос здесь по существу является дубликатом этого вопроса.

Содержимое HTML сообщалось как кодированное UTF-8 и, по большей части, оно было, за исключением одного или двух недействительных недопустимых символов UTF-8.

Это, по-видимому, смущает BeautifulSoup о том, какая кодировка используется, и при попытке сначала декодировать как UTF-8 при передаче содержимого в BeautifulSoup, например это:

soup = BeautifulSoup(response.read().decode('utf-8'))

Я бы получил ошибку:

UnicodeDecodeError: 'utf8' codec can't decode bytes in position 186812-186813: 
                    invalid continuation byte

Более внимательно рассмотрев вывод, был экземпляр символа Ü, который был неправильно закодирован как недопустимая последовательность байтов 0xe3 0x9c, а не правильный 0xc3 0x9c.

Как показывает текущий самый высокий рейтинг по этому вопросу, недопустимые символы UTF-8 могут быть удалены при разборе, так что только достоверные данные передаются в BeautifulSoup

soup = BeautifulSoup(response.read().decode('utf-8', 'ignore'))

Ответ 2

Кодирование результата до utf-8, похоже, работает для меня:

print (soup.find('div', id='navbutton_account')['title']).encode('utf-8')

Это дает:

Hier können Sie sich kostenlos registrieren und / oder einloggen!