Кодировка HTML и анализ lxml

Я пытаюсь, наконец, решить некоторые проблемы с кодировкой, возникающие при попытке очистить HTML с помощью lxml. Вот три примера HTML-документов, с которыми я столкнулся:

1.

<!DOCTYPE html> <html lang='en'> <head> <title>Unicode Chars: 은 —'</title> <meta charset='utf-8'> </head> <body></body> </html> 

2.

 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko-KR" lang="ko-KR"> <head> <title>Unicode Chars: 은 —'</title> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> </head> <body></body> </html> 

3.

 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Unicode Chars: 은 —'</title> </head> <body></body> </html> 

Мой основной скрипт:

 from lxml.html import fromstring ... doc = fromstring(raw_html) title = doc.xpath('//title/text()')[0] print title 

Результаты:

 Unicode Chars: ì ââ Unicode Chars: 은 —' Unicode Chars: 은 —' 

Таким образом, очевидно, проблема с образцом 1 и отсутствующим <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> . Решение здесь правильно распознает образец 1 как utf-8, и поэтому он функционально эквивалентен моему исходному коду.

Документы lxml выглядят конфликтующими:

Отсюда пример кажется, что мы должны использовать UnicodeDammit для кодирования разметки как unicode.

 from BeautifulSoup import UnicodeDammit def decode_html(html_string): converted = UnicodeDammit(html_string, isHTML=True) if not converted.unicode: raise UnicodeDecodeError( "Failed to detect encoding, tried [%s]", ', '.join(converted.triedEncodings)) # print converted.originalEncoding return converted.unicode root = lxml.html.fromstring(decode_html(tag_soup)) 

Однако здесь говорится:

[Y] ou будет получать ошибки при попытке [разбора] данных HTML в строке юникода, которая указывает кодировку в метатеге заголовка. Обычно вам следует избегать преобразования данных XML / HTML в unicode, прежде чем передавать их в синтаксические анализаторы. Он медленнее и подвержен ошибкам.

Если я попытаюсь выполнить первое предложение в документах lxml, теперь мой код:

 from lxml.html import fromstring from bs4 import UnicodeDammit ... dammit = UnicodeDammit(raw_html) doc = fromstring(dammit.unicode_markup) title = doc.xpath('//title/text()')[0] print title 

Теперь я получаю следующие результаты:

 Unicode Chars: 은 —' Unicode Chars: 은 —' ValueError: Unicode strings with encoding declaration are not supported. 

Пример 1 теперь работает корректно, но образец 3 приводит к ошибке из-за <?xml version="1.0" encoding="utf-8"?> .

Есть ли правильный способ справиться со всеми этими случаями? Есть ли лучшее решение, чем следующее?

 dammit = UnicodeDammit(raw_html) try: doc = fromstring(dammit.unicode_markup) except ValueError: doc = fromstring(raw_html) 

2 Solutions collect form web for “Кодировка HTML и анализ lxml”

lxml есть несколько проблем, связанных с обработкой Unicode. Лучше всего использовать байты (на данный момент), явно указывая кодировку символов:

 #!/usr/bin/env python import glob from lxml import html from bs4 import UnicodeDammit for filename in glob.glob('*.html'): with open(filename, 'rb') as file: content = file.read() doc = UnicodeDammit(content, is_html=True) parser = html.HTMLParser(encoding=doc.original_encoding) root = html.document_fromstring(content, parser=parser) title = root.find('.//title').text_content() print(title) 

Вывод

 Unicode Chars: 은 —' Unicode Chars: 은 —' Unicode Chars: 은 —' 

Проблема, вероятно, связана с тем, что <meta charset> является относительно новым стандартом (HTML5, если я не ошибаюсь или раньше не использовался).

До тех пор, lxml.html библиотека lxml.html будет обновлена, чтобы отразить ее, вам нужно будет обработать этот случай специально.

Если вы только заботитесь об ISO-8859- * и UTF-8 и можете позволить отказаться от кодировок, не поддерживающих ASCII (например, UTF-16 или традиционных азиатских кодировок), вы можете сделать замену регулярного выражения на байт string, заменив новый <meta charset> на более старый формат http-equiv .

В противном случае, если вам нужно правильное решение, лучше всего исправить библиотеку самостоятельно (и вносить исправление, пока вы на ней.) Возможно, вы захотите спросить разработчиков lxml, есть ли у них какой-либо полупеченный код, эта конкретная ошибка, или если они отслеживают ошибку в своей системе отслеживания ошибок в первую очередь.

  • Tried Python BeautifulSoup и Phantom JS: STILL не может очищать веб-сайты
  • Предотвращение загрузки CSS / другого ресурса в PhantomJS / Selenium, управляемом Python
  • Scrapy: выбор содержимого через Javascript
  • Как запустить Scrapy из сценария Python
  • Запуск приложения в герою? Что такое procfile? команда «web:»?
  • request.iter_content () получает неполный файл (1024 МБ вместо 1,5 ГБ)?
  • Не удалось правильно записать извлеченные элементы в файл excel?
  • Scrapy необходимо просканировать все следующие ссылки на веб-сайте и перейти к следующей странице
  •  
    Interesting Posts for Van-Lav

    Как получить имена столбцов из строки, возвращенной из запроса adodbapi?

    Сделайте часть названия matplotlib полужирным шрифтом и другим цветом

    mrjob: Неверный путь действия начальной загрузки, должно быть местоположение в Amazon S3

    Подсчитайте количество списков, содержащих определенный элемент во вложенном списке

    Python GTK + 3 Безопасная Threading

    Какую команду использовать для проверки того, является ли python 64-битным или 32-битным

    Можно ли добавить пакеты PyQt4 / PySide в песочницу Virtualenv?

    Запрос на фляжку Python dataurl из url – для декодирования base64 image

    Эволюционный алгоритм механизма ходьбы Теоса Янсена

    Как подклассы str в Python

    Импорт модулей сопоставления в Python для простого рефакторинга

    Показывать значение слайдера kivy при изменении

    Есть ли способ декодировать числовые коды ошибок COM в pywin32

    Failed scipy.special import "Символ не найден: ___addtf3"

    Django Celery отправить электронное письмо электронной почты не работает

    Python - лучший язык программирования в мире.