Saturday, January 28, 2012

«LXML» или как парсить HTML с лёгкостью http://habrahabr.ru/blogs/python/114788/

http://habrahabr.ru/blogs/python/114788/

«LXML» или как парсить HTML с лёгкостью

Описание библиотеки


«lxml» — это библиотека для парсинга, основанная на библиотеках C (Си) libxml2 и libxslt. Она обладает хорошей скоростью работы и простотой использования. Данная библиотека предоставляет «питонический» API и очень схожа на «ElementTree» API.
«lxml» обладает большими плюсами:
  • Соответствие стандартам XML
  • Поддержка невалидного HTML-кода
  • Активная поддержка библиотек экспертами
  • Скорость
  • Многофункциональность
  • Безопасность (нет проблем с памятью)

Единственный минус — данную библиотеку нельзя использовать в «GAE» и в других местах, где используют встроенный python.

Скачать последнюю (2.3) на текущий момент версию можно здесь.
Официальную документацию в PDF-формате можно почитать здесь.

Примеры


Примеры — лучшее средство для изучения чего-либо нового, по-этому я приготовил несколько примеров с подробными комментариями.

Для начала попробуем получить содержимое каких-то HTML-тегов c помощью XPath:
 import lxml.html # подключили библиотеку  doc = lxml.html.document_fromstring("""<html>  <body>    <span class="simple_text">One</span> text</br>    <span class="cyrillic_text">Второй</span> текст</br>  </body> </html> """) # Получили HTML-документ со строки  txt1 = doc.xpath('/html/body/span[@class="simple_text"]/text()[1]') # Находим тег «span», у которого аттрибут «class» равен значению «simple_text» и с помощью функции text() получаем текст элемента txt2 = doc.xpath('/html/body/span[@class="cyrillic_text"]/following-sibling::text()[1]') # Находим тег «span», у которого аттрибут «class» равен значению «cyrillic_text» и получаем следующий за ним текст с помощью following-sibling (получаем следующий в ветке элемент) и text() # Для получение значений использовался XPath 

Получить XPath может помочь «Firebug».


Теперь попробуем что-то более полезное! Выбирем все названия топиков на главной странице «Хабрахабра», но для этого попробуем применить CSS-селекторы:
 import urllib # подключили библиотеку urllib import lxml.html # подключили библиотеку lxml  page = urllib.urlopen("http://habrahabr.ru/") # Открываем наш любимый Хабр  doc = lxml.html.document_fromstring(page.read()) # Получили HTML-код главной страницы Хабра  for topic in doc.cssselect('h2.entry-title a.topic'):     print topic.text         # выводим на экран названия топиков. 

Здесь с помощью метода «cssselect» мы выбрали названия топиков на Хабре.
В отличии от «Beautiful Soup» в «lxml» с кириллицей проблем нет.
Получить значение аттрибута очень просто:
 element.get('href') # мы получили значение атрибута «href» 

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

Заключение


Библиотека «lxml» выше всех похвал! Работать с этой библиотекой — это одно удовольствие.
Библиотека предоставляет удобные методы для парсинга любой сложности. Библиотека работает как с XML, так и с невалидным HTML-кодом. Обладает удобной документацией и множеством примеров.

По моему мнению, библиотека «lxml» — лучшая библиотека для парсинга в Python.

Спасибо пользователю dmzkrsk за дополнение.
+60
2 марта 2011, 19:23
156

комментарии (20)

+5
dmzkrsk2 марта 2011, 20:08#
Единственный «минус» — из-за того, что основана на сишных библиотеках, она недоступен в GAE и в других местах, где используют встроенный python (плагины к xbmc, например). Приходится использовать тот же «Beautiful Soup»
0
Mirgorod2 марта 2011, 20:14#
Спасибо, сейчас добавлю в статью.
0
kurd3 марта 2011, 00:03#
Вообще для полного идеала, хотелось бы, чтобы исходный код, в котором есть комментарии на Кириллице, изначально содержал кодировку. Мелочь, а приятно)
0
mitnlag3 марта 2011, 21:08#
Измучился я с этой кодировкой, знаете ли. Py 2.7 под виндой в консоли при попытке напечатать node.text вываливается с ошибкой, что не смог декодировать в 866. Перевел консоль в utf — chcp 65001 — один фиг.
+1
loststylus3 марта 2011, 00:59#
Да суп и поудобнее будет, имхо.
0
danSamara3 марта 2011, 22:20#
Удобнее — факт, но почему-то глючнее. Продвигается как парсер, хорошо работающий с «кривым» HTML, но почему-то не очень хорошо работает с «прямым». LXML давал результаты лучше.
0
niksite5 марта 2011, 18:50#
А ещё на порядок тормознее и глючнее. Да, за удобство приходится много платить.
+3
kurd2 марта 2011, 20:56#
Хотел поставить + в карму, но уже делал это. Спасибо за топик. Хочется, чтобы как можно было статей про Python.
0
tvaleev2 марта 2011, 22:36#
Помню по работе нужно было распарсить большой и кривой html-документ с отзывами клиентов. По незнанию взялся за Beautiful Soup. Но очень быстро пришел к lxml. Оказался идеальным для таких задач.
+1
cyberklin2 марта 2011, 22:38#
lxml действительно очень хороша. жалко, что автор так вкратце описал возможности :)

кроме того, что указано в статье из ключевых моментов хотелось бы отметить, что библиотека поддерживает iterparse, что позволяет легко, быстро и с небольшими затратами памяти обрабатывать реально большие xml-документы (2гб файл — 10сек, память не более 7М).

ещё про lxml: www.ibm.com/developerworks/xml/library/x-hiperfparse/
0
inikulin2 марта 2011, 23:11#
html != xml. Как я понимаю, сие не осилит, например, аттрибуты без значений.
0
Mirgorod2 марта 2011, 23:28#
В документации написано, что lxml умеет работать с невалидным html, но всё же всему есть предел (:
0
inikulin3 марта 2011, 02:13#
Аттрибуты без значений — это валидный html и не валидный xhtml. Насущный html5 в частности не является подмножеством xhtml.
+1
seriyPS3 марта 2011, 02:38#
Прекрасно понимает. Точнее даже исправляет
 >>> import lxml.html >>> from lxml import etree >>> >>> doc = lxml.html.document_fromstring("<html><input type=radio checked></html>") >>> etree.tostring(doc) '<html><body><input type="radio" checked="checked"/></body></html>' >>> input=doc.xpath("//*[@checked='checked']")[0] >>> input.tag 'input' >>> input.attrib {'checked': 'checked', 'type': 'radio'}
+1
zxmd3 марта 2011, 00:03#
Отличная библиотека, отличная статья.
+1
seriyPS3 марта 2011, 02:50#
Тока не надо воспринимать совет «Получить XPath может помочь «Firebug»» как прямое руководство к действию. Firebug может ПОМОЧЬ получить XPath, но тупо копировать то, что он выдает не нужно. Потому что для списка статей с главной хабра он выдаст
/html/body/div[2]/div/div/div
вместо
//div[@id='main-content']/div

И это еще простой пример а то бывают и подлиннее habrahabr.ru/blogs/php/114323/#comment_3688976

Разница в том, что первый гораздо легче поломать изменением разметки хабра при очередном обновлении

К тому же FireBug в таблицы всегда добавляет тег tbody независимо от того есть он в разметке или нет.
+1
wibotwi3 марта 2011, 03:45#
lxml чудесная либа
с версией непонятно, в индексе она 2.3, но на их сайте последняя версия 2.3beta1
хотя я активно пользуюсь этой бетой и всё ок работает
в 2.2 слишком много багов
+1
hellman3 марта 2011, 07:48#
По сравнению с топиком про Beautiful Soup примеров маловато.
0
danSamara3 марта 2011, 22:24#
Кто-нибудь кинет ссылочкой на хорошие примеры по XPath? Только не из серии «XPath за 24 часа», а что-нить по-брутальнее ;)
0
wRAR4 марта 2011, 21:54#
Я просто взял спеку.

No comments:

Post a Comment