9 марта 2011 в 09:43

Простая библиотека для парсинга HTML

Недавно зарелизил Leaf, это небольшая библиотека для парсинга HTML на Python.
Она уже довольно долгое время покрывает все мои нужды в парсинге, но еще есть идеи для развития.
Эта библиотека по сути обертка над lxml, которая делает работу с ней гораздо приятней.

Функции


  • Удобный доступ к CSS селекторам как в jQuery
  • Простой доступ к атрибутам элемента
  • Возможность конвертации HTML в другие языки разметки (bbcode, markdown и т.д.)
  • Несколько функций для работы с текстом
  • Ну и конечно же все функции самой lxml

Описание


Чтобы распарсить html, нужно передать строку с ним в leaf.parse:
  1. import leaf  
  2. document = leaf.parse(sample)
  3. links = document('div#menu a') # получаем ссылки из меню через CSS селекторы
  4. link = document.get('div#menu a') # получаем первую ссылку или None (если ссылок нет)
* This source code was highlighted with Source Code Highlighter.

Кроме того, теперь доступ к атрибутам элемента теперь более удобный:
  1. print link.onclick
  2. print link.id
* This source code was highlighted with Source Code Highlighter.

Все стандартные методы lxml доступны (и элементы полученные в результате их выполнения сохраняют все преимущества библиотеки):
  1. link = document.xpath('body/div/ul/li[@class="active_link"]')[0]
  2. link.get('a').text
* This source code was highlighted with Source Code Highlighter.

Ну и пожалуй самая интересная функциональность это конвертация html в bbcode и другие языки разметки. В будущем будут добавленны методы для конвертации в популярные языки разметки, а пока можно очень просто написать функцию для нужного метода.
  1.   # Пример функции конвертора из html в некий
  2.     # язык разметки, который поддерживает только
  3.     # ссылки заключенные в [url][/url]
  4.   def omgcode_formatter(element, children):
  5.     # Заменяем <br> на символ перевода строки
  6.     if element.tag == 'br':
  7.       return '\n'
  8.     # Засовываем ссылки в [url][/url]
  9.     if element.tag == 'a':
  10.       return u"[url=link}]{text}[/url]".format(link=element.href, text=children)
  11.     # Для всех остальных элементов возвращаем результат
  12.         # обработки всех детей.
  13.     if children:
  14.       return children
* This source code was highlighted with Source Code Highlighter.

Данная функция будет вызванна рекурсивно, принимая в качестве параметров element (это элемент html тега) и children (результат выполнения данной функции на всех детях данного элемента).
Чтобы сконвертировать элемент (к слову, можно использовать как отдельный слой, так и все дерево):
  1. document.parse(omgcode_formatter)
* This source code was highlighted with Source Code Highlighter.

где document — это объект класса leaf.Parser.
Ну, и пара функций для работы с текстом:

to_unicode — Переводит строку в юникод
strip_accents — Убирает из строки ударения, умляуты и подобные вещи
strip_symbols — Убирает из строки разные юникодные спец. символы и прочее
strip_spaces — Удаляет лишние пробелы
strip_linebreaks — Удаляет лишние переводы строк

Более подробные примеры есть в тестах.

Заключение


Библиотека доступна на:
pingu @pingu
карма
1,0
рейтинг 0,0
Самое читаемое Разработка

Комментарии (27)

  • +1
    Чем лучше packages.python.org/pyquery/?
    • 0
      вот тоже очень интересует
    • +1
      Как минимум в pyquery нет конвертации в другой язык разметки. И не уверен насчет .get. Вообще, когда писалась библиотека из распространненых парсеров были только BeautifulSoup и lxml, первый довольно кривой, для второго и была написана эта обертка. Плюс, субъективно эта библиотека проще.
      • 0
        Плюс, как мне показалось pyquery больше направлен на модификацию html, а leaf на сам парсинг.
      • 0
        А сейчас реализована конвертация в markdown?
        Было бы интересно получить функцию наподобие «упростить форматирование» из эвернота.
        • 0
          Сейчас в планах есть написать готовые конверторы для популярных языков разметки, а пока можно написать самому. Там получаются очень простые конверторы. Если сильно нужно, могу сегодня сделать markdown.
          • 0
            Просто я не уверен, что можно так в лоб перегонять. Сходу только вложенные p и списки на ум приходят. В таком случае нужно поверх написать prettify-катор. Да и со стилями опять же могут быть проблемы.
            Те конвертеры которые я видел не делают ничего хорошого из реальной страницы.
            Библиотека однозначно удобная по синтаксису.
            • 0
              Ну, если в странице болд отображается через:
              <p class="text-bold">blah</p>

              то такой текст довольно сложно сконвертировать. В остальных же случаях все более-менее нормально.
              • 0
                Буквально 3 дня назад писал конвертер html -> bbcode, учесть всё и везде не так просто, буду следить за развитием этого проекта. Спасибо за наводку.

                PS Вот например минимум 3 способа отцентровать текст в html:
                <div align="center">
                <div style="text-align: center">
                <center>
                • +1
                  Только вот вроде в markdown нет ни одного такого способа :3
                  Плюс как правило стили учитывать не обязательно. Главное получить читабельный текст с форматированием, а не всю верстку.
                  • 0
                    Eсли нужен только текст: text = re.sub('<.+?>', '', html)
                    Я считаю что нужно максимально приблизить вёрстку к оригиналу, хотя я согласен, что случаи бывают разные.
                    • 0
                      Ну, верстка будет такой же как и в оригинале, а стили это уже немного другое. Как и расположение элементов (align, etc)
  • –1
    Ты конечно молодец,
    но такое уже есть!
  • +7
    Хорошая штука!

    Вот не пойму я комментаторов выше. Стоит кому-то предложить хорошую альтернативу XXX, так сразу набегает толпа комментаторов с выкриками «не нужно! велосипед!».
    • +4
      Это не «велосипед», это как-бы такой толстый запрос на сравнение по функционалу или производительности, чтобы определиться переходить или нет. Ну я так считаю.
      • 0
        Производительность такая же, как у lxml, там оверхед очень маленький.
    • 0
      Причем тут не нужно? Есть две обертки поверх lxml, интересно их сравнение, а так же что сподвигло автора написать свою.
      • 0
        Мне кажется я уже ответил на это выше
  • –4
    Не знаю в чем проблема. Если нужно просты выдергивать значения, если сделать регепс, то он будет работать. Данные штуки, скорее всего, для тех кто не понимает регепсы. Ну я так считаю, если не прав, поправьте…
    • +5
      Вообще, парсить различные языки разметки, такие как html регекспами считается плохим тоном, т.к. такие парсеры очень легко ломаются при изменении страницы.
      Также, парсер сделанный для определенной разметки будет работать быстрее, чем для любой.
      Да и при использовании html парсеров код получается красивей, чище и более легко поддерживается.
  • +1
    Да ладно вам, нормальная библиотека. Она решает свою конкретную задачу.

    Не в качестве альтернативы, а как расширение кругозора — есть еще Scrapy. Он тоже вполне неплох для парсинга
    • 0
      Scrapy это немного из другой оперы))) Ничто не мешает использовать этот Leaf внутри Scrapy. А Scrapy он в основном именно как платформа для скачивания страничек. Чем ты их будешь парсить ему все равно. А так да, Scrapy обалденный ^_^.
    • 0
      scrapy использует для парсинга libxml2 либо lxml.
  • 0
    Обертка для обертки, но в любом случае на заметку возьмем.
  • 0
    По поводу генерации XML в другие языки разметки — тут лаконичнее смотрелся бы XSLT. По крайней мере он больше контроля дает. К примеру для генерации bb кодов я бы такого плана шаблон наваял вместо вашего примера:

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet version = "1.0"	xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    	<xsl:template match="br">
    <xsl:text>
    </xsl:text>
    	</xsl:template>
    
    	<xsl:template match="a[@href]">[url=<xsl:value-of select="@href"/>]<xsl:apply-templates />[/url]</xsl:template>
    </xsl:stylesheet>


    Возможно выглядит неубедительно, но при увеличении количества правил общая сложность понимания почти не растет. Я, например, для эксперимента дописал шаблоны для превращения ul/li и ol/li в Markdown * и #, заголовков в "==", b,i,u тегов в их BB-code аналоги и защиту от script инъекций — уложился в ~50 строк.
    • 0
      не думаю что тут получится сильно больше. зато python читаемее xslt, как по мне.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.