Извлечение данных стандартной библиотекой

Иногда нужные данные можно достать без тяжёлых библиотек — стандартными средствами Python.
Для простых задач — вытащить все ссылки, найти e-mail, очистить текст — хватает модулей re и html.parser из стандартной библиотеки. Они работают где угодно, включая браузер.

Прежде чем тянуть BeautifulSoup, полезно понять, как извлекать данные «руками». Это и быстрее для мелких задач, и даёт интуицию о том, что библиотеки делают за тебя. Начнём с извлечения всех ссылок из HTML с помощью стандартного html.parser — этот код реально работает в нашем раннере.

Попробуй сам ▶

from html.parser import HTMLParser

page = '''
<ul>
  <li><a href="/python">Python</a></li>
  <li><a href="/sql" class="hot">SQL</a></li>
  <li><a href="https://ext.example">Внешняя</a></li>
</ul>
'''

class LinkGrabber(HTMLParser):
    def __init__(self):
        super().__init__()
        self.links = []
    def handle_starttag(self, tag, attrs):
        if tag == 'a':
            href = dict(attrs).get('href')
            if href:
                self.links.append(href)

g = LinkGrabber()
g.feed(page)
print('Найдено ссылок:', len(g.links))
for href in g.links:
    print(' ', href)

Когда уместны регулярные выражения

Регулярки (re) хороши для поиска плоских шаблонов внутри текста: e-mail, телефоны, цены, даты. Они плохи для разбора вложенной структуры HTML, но отлично дополняют парсер на этапе «чистки» уже извлечённого текста. Достанем все e-mail из текста:

Попробуй сам ▶

import re

text = 'Пишите на [email protected] или [email protected], спам на [email protected]'

pattern = r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'
emails = re.findall(pattern, text)

print('e-mail на странице:')
for e in sorted(set(emails)):
    print(' ', e)

Как работает под капотом

re.findall сканирует строку и возвращает все непересекающиеся совпадения шаблона. Шаблон e-mail читается так: «один или больше допустимых символов, затем @, затем домен, точка и зона из 2+ букв». Важно понимать: регулярка ничего не знает о тегах и вложенности — она видит только плоский текст.

Очистка и нормализация текста

Текст, извлечённый из HTML, почти всегда «грязный»: переводы строк, неразрывные пробелы, табуляции, лишние отступы из вёрстки. Прежде чем сохранять значение, его нормализуют. Базовый набор приёмов — strip() убирает крайние пробелы, ' '.join(text.split()) схлопывает любые пробельные последовательности в один пробел, а re.sub вычищает всё лишнее по шаблону. Для цен и чисел отдельно оставляют только цифры и приводят к int или float.

Стандартная библиотека сильнее, чем кажется. Модуль html умеет раскодировать HTML-сущности: html.unescape('&amp;nbsp;') превратит закодированные символы обратно в нормальные. Модуль unicodedata помогает с экзотическими пробелами и диакритикой. Связка «парсер достаёт фрагмент → re и строковые методы чистят его → приведение типа» закрывает подавляющее большинство задач извлечения, не требуя ни одной внешней зависимости.

Частые ошибки

  • Парсить вложенный HTML регуляркой. Классический антипаттерн: попытка вытащить содержимое тегов регуляркой ломается на любой нестандартной разметке.
  • Жадные квантификаторы. .* может «съесть» слишком много — используй нежадные .*? или точные классы символов.
  • Не дедуплицировать. Один e-mail может встретиться многократно — set() убирает дубли.

Best practices

  • Регулярки — для плоских шаблонов в тексте; структуру HTML разбирай парсером.
  • Тестируй шаблон на реальных и «грязных» данных, а не только на идеальном примере.
  • Комбинируй: парсер достаёт нужный блок, регулярка чистит и валидирует значение внутри.

Хорошая дисциплина — отделять извлечение от валидации. Сначала достаём кандидата (например, строку, похожую на цену), затем отдельно проверяем его регуляркой или приведением типа и решаем, что делать с «битыми» значениями: пропустить, записать None или залогировать. На реальных страницах всегда найдётся карточка-исключение, и устойчивый скрейпер не падает на ней, а аккуратно её обрабатывает. Такой подход — «извлёк, проверил, нормализовал» — делает результат предсказуемым даже на грязных и неоднородных данных.

Итог: стандартные html.parser и re решают много задач без внешних зависимостей. Парсер — для структуры, регулярки — для плоских шаблонов. Это фундамент, на котором стоят большие библиотеки.

Проверьте себя
1. Для какой задачи регулярные выражения подходят ХОРОШО?
AРазбор вложенной структуры HTML
BПоиск плоских шаблонов в тексте: e-mail, телефоны, даты
CПостроение DOM-дерева
DРендеринг JavaScript
2. Зачем оборачивать список найденных e-mail в set()?
AЧтобы отсортировать по алфавиту
BЧтобы убрать повторяющиеся адреса (дедупликация)
CЧтобы ускорить регулярку
Dset() обязателен в re