Инструменты Python для обработки HTML документов
Обзор библиотек для работы с HTML в Python
Как выполнять гибкий и удобный парсинг HTML с использованием BeautifulSoup и парсера lxml?
Основной и наиболее популярный подход - комбинация библиотеки BeautifulSoup с парсером lxml. BeautifulSoup предоставляет высокоуровневые методы для навигации, поиска и изменения HTML-дерева, а lxml обеспечивает высокую скорость парсинга и устойчивость к некорректной разметке.
from bs4 import BeautifulSoup
html_doc = '''
<html><body><h1>Заголовок</h1><p class="text">Пример текста</p></body></html>
'''
soup = BeautifulSoup(html_doc, 'lxml')
print(soup.h1.text) # Заголовок
print(soup.find('p', class_='text').text) # Пример текстаPython html библиотека (библиотеки python для работы с html (beautifulsoup, lxml и др.))
Выбор парсера lxml даёт преимущество в скорости и точности. Для установки необходим пакет lxml.
pip install lxml beautifulsoup4
Типичная ошибка: если lxml не установлен, BeautifulSoup автоматически переключится на встроенный парсер html.parser, что может привести к иной интерпретации сложных структур или к снижению производительности. Решение - всегда явно указывать парсер и убедиться, что он доступен.
Как использовать lxml напрямую без BeautifulSoup для парсинга HTML?
Библиотека lxml может работать самостоятельно через модуль lxml.html. Это даёт прямой доступ к XPath и CSS-селекторам, а также максимальную скорость.
from lxml import html
doc = html.fromstring('<div id="content">Текст</div>')
result = doc.xpath('//div[@id="content"]/text()')
print(result) # ['Текст']
Цель: когда требуется минимальное количество промежуточных слоёв, например, при обработке больших объёмов данных.
Проблема: lxml не прощает невалидную разметку так же легко, как BeautifulSoup. Некоторые некорректные атрибуты могут вызвать ошибки парсинга. Решение - использовать parser = html.HTMLParser(remove_blank_text=True) для мягкой обработки.
Как выполнить парсинг HTML без установки дополнительных библиотек - с помощью встроенного парсера html.parser?
Модуль html.parser входит в стандартную библиотеку Python. Он прост, но не такой гибкий и быстрый как lxml или html5lib. Подходит для простых и небольших задач.
from html.parser import HTMLParser
class MyParser(HTMLParser):
def handle_data(self, data):
print('Найден текст:', data)
parser = MyParser()
parser.feed('<p>Простой текст</p>')
Случай использования: когда нельзя устанавливать сторонние пакеты (например, в ограниченной среде).
Недостаток: приходится писать собственные обработчики событий, что усложняет код. Для поиска по CSS-классам потребуется дополнительная логика.
Как ускорить парсинг за счёт selectolax?
Библиотека selectolax (обёртка над Modest) обеспечивает очень высокую скорость работы с CSS-селекторами. Она не поддерживает XPath, но отлично справляется с задачей извлечения элементов по селекторам из больших HTML-файлов.
from selectolax.parser import HTMLParser
parser = HTMLParser('<div class="item">Значение</div>')
node = parser.css_first('.item')
print(node.text()) # Значение
Цель: получение максимальной производительности при пакетной обработке страниц.
Проблема: selectolax не поддерживает сложные CSS-селекторы (например, :has) и не умеет работать с некорректными атрибутами. Решение - предварительно очищать HTML или использовать другой парсер для сложных случаев.
Как получить более удобный API, похожий на jQuery, с помощью pyquery?
Библиотека pyquery позволяет использовать синтаксис jQuery для работы с HTML. Она основана на lxml, поэтому наследует его скорость.
from pyquery import PyQuery as pq
doc = pq('<ul><li class="item">Элемент</li></ul>')
print(doc('.item').text()) # Элемент
Случай использования: для разработчиков, привыкших к jQuery, и при необходимости цепочных вызовов.
Проблема: pyquery может быть избыточной для простых задач. При работе с некорректным HTML некоторые методы могут вести себя неожиданно.
Рассмотрим расширенные примеры, демонстрирующие решение типичных и неочевидных задач при парсинге HTML.
# Пример 1: Извлечение таблицы с объединёнными ячейками (rowspan/colspan) с помощью BeautifulSoup
from bs4 import BeautifulSoup
html = '''
<table>
<tr><td rowspan="2">A</td><td>B</td></tr>
<tr><td>C</td></tr>
</table>
'''
soup = BeautifulSoup(html, 'lxml')
rows = soup.find_all('tr')
for row in rows:
cells = row.find_all('td')
for cell in cells:
print(cell.get('rowspan', '1'), cell.text)
# Результат: 2 A, 1 B, 1 C
2 A 1 B 1 C
# Пример 2: Использование XPath с lxml для выборки по тексту
from lxml import html
doc = html.fromstring('<div><p>Привет</p><p>Мир</p></div>')
result = doc.xpath('//p[contains(text(), "Мир")]/text()')
print(result) # ['Мир']
['Мир']
# Пример 3: Обработка ошибок кодировки при загрузке страницы с requests и BeautifulSoup
import requests
from bs4 import BeautifulSoup
url = 'http://example.com'
try:
resp = requests.get(url, timeout=5)
resp.encoding = resp.apparent_encoding # автоопределение кодировки
soup = BeautifulSoup(resp.text, 'lxml')
print(soup.title.text)
except requests.exceptions.RequestException as e:
print('Ошибка сети:', e)
except Exception as e:
print('Ошибка парсинга:', e)
Example Domain
# Пример 4: Парсинг HTML с использованием сессии и куки
import requests
from bs4 import BeautifulSoup
session = requests.Session()
session.get('http://example.com/login')
resp = session.get('http://example.com/dashboard')
soup = BeautifulSoup(resp.content, 'lxml')
username = soup.select_one('.user-name').text
print(f'Пользователь: {username}')
Пользователь: admin
# Пример 5: Обработка бесконечно вложенных списков с помощью рекурсии
from bs4 import BeautifulSoup
html = '<ul><li>1<ul><li>1.1</li></ul></li><li>2</li></ul>'
soup = BeautifulSoup(html, 'lxml')
def extract(ul):
items = []
for li in ul.find_all('li', recursive=False):
text = li.contents[0].strip() if li.contents else ''
sub = li.find('ul')
items.append({'text': text, 'children': extract(sub) if sub else []})
return items
result = extract(soup.ul)
print(result)
[{'text': '1', 'children': [{'text': '1.1', 'children': []}]}, {'text': '2', 'children': []}]
# Пример 6: Использование html5lib для максимально точного парсинга (соответствие стандарту WHATWG)
from bs4 import BeautifulSoup
# html5lib очень медленный, но точный
soup = BeautifulSoup('<p></p>', 'html5lib')
print(soup.p) # закрывающий тег будет добавлен корректно
<p></p>