Введение в библиотеку Python lxml
lxml — это библиотека, которая позволяет легко обрабатывать XML и HTML файлы, а также может использоваться для парсинга веб-страниц. Существует множество готовых парсеров XML/HTML, но для получения лучших результатов или при определенных задачах разработчики вынуждены писать свои собственные парсеры. Это как раз та ситуация когда возникает необходимость в lxml библиотеке. Ключевые преимущества этой библиотеки заключаются в том, что она проста в использовании, чрезвычайно быстра при анализе больших документов, очень хорошо документирована и обеспечивает простое преобразование исходных данных в типы данных Python, что упрощает манипулирование файлами.
В этом руководстве мы рассмотрим библиотеку lxml, начнем с того, как установить ее для различных операционных систем, а затем обсудим ее преимущества и широкий спектр функциональных возможностей, которые она предлагает.
Установка
Есть несколько способов установить lxml в вашей системе. Мы рассмотрим некоторые из них ниже.
Используя Pip
Pip — это менеджер пакетов Python, который с легкостью загружает и устанавливает библиотеки Python в вашу локальную систему, он также загружает и устанавливает все зависимости для устанавливаемого пакета.
Если в вашей системе установлен pip, просто запустите следующую команду в терминале или командной строке:
$ pip install lxml
Используя apt-get
Если вы используете MacOS или Linux, вы можете установить lxml, выполнив эту команду в своем терминале:
$ sudo apt-get install python-lxml
Используя easy_install
Вы, вероятно, не дойдете до этой варианта, но если по какой-то причине ни одна из вышеперечисленных команд не сработает, попробуйте использовать easy_install:
$ easy_install lxml
Примечание. Если вы хотите установить какую-либо конкретную версию lxml, вы можете просто указать ее при запуске команды в командной строке или терминале, например, lxml == 3.x.y.
Теперь у вас должна быть локальная копия библиотеки lxml, установленной на вашем компьютере. Давайте теперь запачкаем руки и посмотрим, какие классные вещи можно сделать с помощью этой библиотеки.
Функциональность
Чтобы иметь возможность использовать библиотеку lxml в вашей программе, вам сначала нужно ее импортировать. Вы можете сделать это с помощью следующей команды:
from lxml import etree as et
Это строка импортирует модуль etree из библиотеки lxml.
Создание документов HTML/XML
Используя модуль etree, мы можем создавать элементы XML/HTML и их подэлементы, что очень полезно, если мы пытаемся написать или манипулировать файлом HTML или XML. Давайте попробуем создать базовую структуру файла HTML, используя etree:
root = et.Element('html', version="5.0") # Передайте родительский узел, имя дочернего узла, # и любое количество дополнительных атрибутов et.SubElement(root, 'head') et.SubElement(root, 'title', bgcolor="red", fontsize='22') et.SubElement(root, 'body', fontsize="15")
В приведенном выше коде, функция Element требует как минимум один параметр, тогда как функция SubElement требует как минимум два. Это связано с тем, что функция Element «требует» только имя создаваемого элемента, тогда как функция SubElement требует имя как корневого узла, так и дочернего узла, который будет создан.
Также важно знать, что обе эти функции имеют только нижнюю границу для количества аргументов, которые они могут принять, но не имеют верхней границы, поэтому можете передать им столько атрибутов, сколько захотите. Чтобы добавить атрибут к элементу, просто добавьте дополнительный параметр в функцию (Sub) Element и укажите свой атрибут в виде attributeName = ‘attribute value’.
Давайте попробуем запустить код, который мы написали выше, чтобы лучше понять эти функции:
# Используйте pretty_print = True для отступов в HTML print (et.tostring(root, pretty_print=True).decode("utf-8"))
Результат:
<html version="5.0"> <head/> <title bgcolor="red" fontsize="22"/> <body fontsize="15"/> </html>
Есть еще один способ создания и организации элементов в иерархической манере:
root = et.Element('html') root.append(et.SubElement('head')) root.append(et.SubElement('body'))
В этом случае всякий раз, когда мы создаем новый элемент, мы просто добавляем его в корневой/родительский узел.
Парсинг HTML / XML документов
До сих пор мы рассматривали только создание новых элементов, присвоение им атрибутов и т. д. Теперь рассмотрим пример, в котором у нас уже есть файл HTML или XML, и мы хотим проанализировать его для извлечения определенной информации. Предполагая, что у нас есть файл HTML, который мы создали в первом примере, давайте попробуем получить имя тега одного конкретного элемента, а затем распечатать имена тегов всех элементов.
print(root.tag)
Результат:
html
Теперь, чтобы перебрать все дочерние элементы в корневом узле и напечатать их теги используем следующий код:
for e in root: print(e.tag)
Результат:
head title body
Работа с атрибутами
Давайте теперь посмотрим, как мы связываем атрибуты с существующими элементами, а также как извлекать значение определенного атрибута для данного элемента.
Используя тот же корневой элемент, что и раньше, попробуйте следующий код:
root.set('newAttribute', 'attributeValue') # Распечатываем root снова, чтобы увидеть, был ли добавлен новый атрибут print(et.tostring(root, pretty_print=True).decode("utf-8"))
Результат:
<html version="5.0" newAttribute="attributeValue"> <head/> <title bgcolor="red" fontsize="22"/> <body fontsize="15"/> </html>
Здесь мы видим, что newAttribute = «attributeValue» действительно был добавлен к корневому элементу.
Давайте теперь попробуем получить значения атрибутов, которые мы установили в приведенном выше коде. Мы можем получить доступ к дочернему элементу, используя индексирование массива корневого элемента, а затем используем метод get() для получения атрибута:
print(root.get('newAttribute')) print(root[1].get('alpha')) # root[1] accesses the `title` element print(root[1].get('bgcolor'))
Результат:
attributeValue None red
Извлечение текста из элементов
Теперь, когда мы увидели основные функции модуля etree, давайте попробуем сделать еще несколько интересных вещей с нашими файлами HTML и XML. Почти всегда эти файлы содержат текст между тегами. Итак, давайте посмотрим, как мы можем добавить текст к нашим элементам:
# Копирование кода из самого первого примера root = et.Element('html', version="5.0") et.SubElement(root, 'head') et.SubElement(root, 'title', bgcolor="red", fontsize="22") et.SubElement(root, 'body', fontsize="15") # Добавить текст в Elements и SubElements root.text = "This is an HTML file" root[0].text = "This is the head of that file" root[1].text = "This is the title of that file" root[2].text = "This is the body of that file and would contain paragraphs etc" print(et.tostring(root, pretty_print=True).decode("utf-8"))
Результат:
<html version="5.0">This is an HTML file<head>This is the head of that file</head><title bgcolor="red" fontsize="22">This is the title of that file</title><body fontsize="15">This is the body of that file and would contain paragraphs etc</body></html>
Проверяем, есть ли у элемента дочерние элементы
Далее, есть две очень важные вещи, которые мы должны быть в состоянии проверить, поскольку это требуется во многих веб-приложениях для обработки исключений. Во-первых, мы хотели бы проверить, есть ли у элемента дочерние элементы, а во-вторых, является ли текущий узел элементом.
Давайте сделаем это для узлов, которые мы создали выше:
if len(root) > 0: print("True") else: print("False")
Приведенный выше код выведет True, поскольку у корневого узла есть дочерние узлы. Однако, если мы проверим то же самое для дочерних узла корня, как в коде ниже, результат будет False.
for i in range(len(root)): if (len(root[i]) > 0): print("True") else: print("False")
Результат:
False False False
Теперь давайте сделаем то же самое, чтобы увидеть, является ли каждый из узлов Element или нет:
for i in range(len(root)): print(et.iselement(root[i]))
Результат:
True True True
Метод iselement полезен для определения, есть ли у вас действительный объект Element, и, таким образом, можете ли вы продолжить его обход, используя методы, которые мы здесь показали.
Проверка, есть ли у элемента родитель
Только что мы показали, как пройти по иерархии, т.е. как проверить, есть ли у элемента дочерние элементы или нет, и теперь в этом разделе мы попытаемся пройти по иерархии вверх, т.е. как проверить и получить родительский элемент для дочернего узла. ,
print(root.getparent()) print(root[0].getparent()) print(root[1].getparent())
Первая строка не должна ничего возвращать (т.е. вернуть None), так как сам корневой узел не имеет родителя. Две других строки должны вернуть корневой элемент, т. е. тег HTML. Давайте проверим вывод, чтобы убедиться, что это то, что мы ожидаем:
Результат:
None <Element html at 0x1103c9688> <Element html at 0x1103c9688>
Извлечение у элементов соседних элементов (sibling)
В этом разделе мы узнаем, как перемещаться вбок по иерархии, которая извлекает родственные элементы элемента в дереве.
Обход дерева в бок очень похож на навигацию по вертикали. Ранее мы использовали getparent, теперь мы будем использовать функции getnext и getprevious. Давайте попробуем их на узлах, которые мы ранее создали, чтобы увидеть, как они работают:
# root[1] должен быть тег `title` print(root[1].getnext()) # Тег после `title` print(root[1].getprevious()) # Тег до `title`
Результат:
<Element body at 0x10b5a75c8> <Element head at 0x10b5a76c8>
Здесь вы можете видеть, что root[1].getnext() извлекает тег «body», так как это был следующий элемент, а root[1].getprevious() извлекает тег «head».
Точно так же, если бы мы использовали функцию getprevious в root, она должна была бы вернула None, и если бы мы использовали функцию getnext в root[2], она также должна была бы вернуть None.
Парсинг XML из строки
Двигаемся дальше, допустим у нас есть файл XML или HTML и мы хотим проанализировать необработанную строку, чтобы получить необходимую информацию или манипулировать ею. Для этого мы можем сделать следующее:
root = et.XML('<html version="5.0">This is an HTML file<head>This is the head of that file</head><title bgcolor="red" fontsize="22">This is the title of that file</title><body fontsize="15">This is the body of that file and would contain paragraphs etc</body></html>') root[1].text = "The title text has changed!" print(et.tostring(root, xml_declaration=True).decode('utf-8'))
Результат:
<?xml version='1.0' encoding='ASCII'?> <html version="5.0">This is an HTML file<head>This is the head of that file</head><title bgcolor="red" fontsize="22">The title text has changed!</title><body fontsize="15">This is the body of that file and would contain paragraphs etc</body></html>
Как видите, мы успешно изменили текст в XML-документе.
Поиск элементов
Последнее, что мы собираемся рассмотреть, то что часто используется при парсинге файлов XML и HTML. Мы рассмотрим способ поиска элементов и получение его текстового содержимого.
print(root.find('a')) # Не существует тегов <a> поэтому будет `None` print(root.find('head').tag) print(root.findtext('title')) # Получение текста из тега title
Результат:
None head This is the title of that file
Заключение
В данной статье мы начали с базового знакомства с тем, что такое библиотека lxml и для чего она используется. После этого мы узнали, как установить ее в различных средах, таких как Windows, Linux и т. д. Продолжив, мы рассмотрели различные функциональные возможности этой библиотеки, которые могут помочь при парсинге HTML/XML файлов.
Оригинальная статья: (Guest Contributor) Introduction to the Python lxml Library
В создании документов HTML/XML поправьте root.append(et.SubElement(‘head’)) -> root.append(et.Element(‘head’)) . И вторую строку также
А если надо парсить сайт, а не страницу?