Как правильно парсить utf-8 в lxml?

В соответствии с документацией lxml (+ вопрос на SO), на вход lxml.html.fromstring() необходимо подавать нераскодированную строку, поскольку lxml сам будет пытаться определять кодировку, а в противном случае, при наличии недопустимых символов в уже раскодированной строке, будет поднимать такую ошибку:
ValueError: Unicode strings with encoding declaration are not supported. Please use bytes input or XML fragments without declaration.

Сделал как рекомендовано, но столкнулся с проблемой, когда некоторые символы не обрабатываются корректно. При этом всё нормально, если вручную раскодировать строку:
>>> bs = b'Hyv\xc3\xa4 juoni!'
>>> lxml.html.fromstring(bs).text
'Hyvä juoni!'
>>> lxml.html.fromstring(bs.decode()).text
'Hyvä juoni!'

Собственно вопрос, как заставить lxml корректно декодировать utf-8?
  • Вопрос задан
  • 5769 просмотров
Пригласить эксперта
Ответы на вопрос 3
@766dt Автор вопроса
Вобщем, проблема оказалась в странном поведении chardet:
>>> cchardet.detect('Hyvä juoni'.encode())
{'confidence': 0.8032709360122681, 'encoding': 'WINDOWS-1252'}
>>> cchardet.detect('Hyv juoni'.encode())
{'confidence': 0.0, 'encoding': 'ASCII'}
>>> cchardet.detect('ä'.encode())
{'confidence': 0.5049999952316284, 'encoding': 'UTF-8'}

Пока лучшее, что придумал, это вручную задавать кодировку контента, если она известна.
fragment = fromstring(content, parser = lxml.html.HTMLParser(encoding='utf-8'))

А вот если кодировка неизвестна, и при этом неверно определяется при помощи chardet, то решения я пока не вижу.
Ответ написан
Комментировать
@abcd0x00
Если кодировка не объявлена, откуда он узнает, что там utf-8?
Декодируй до передачи.
>>> import lxml.html
>>> 
>>> s = b'<div>Hyv\xc3\xa4 juoni!</div>'.decode('utf-8')
>>> 
>>> doc = lxml.html.fromstring(s)
>>> doc
<Element div at 0xb744be3c>
>>> doc.text
'Hyvä juoni!'
>>>
Ответ написан
@ars_kushaet_mars
Нужно просто убрать объявление кодировки в начале страницы (в ошибке как раз написано, что проблема с объявлением кодировки).

Например:
tree = lxml.html.fromstring(resp.text.replace("<?xml version=\'1.0\' encoding=\'UTF-8\'?>", ""))

После этого Вы сможете спарсить данные
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы