</>
Тема 5 из 5

Очистка и изменение HTML

Страницы редко бывают «чистыми»: реклама, скрипты, счётчики, лишние блоки. BeautifulSoup умеет не только читать дерево, но и менять его: удалять, заменять, добавлять теги — а затем получить текст или новый HTML.

Удаление

for tag in soup.select("script, style, .ad"):
    tag.decompose()          # удалить тег со всем содержимым
  • decompose() уничтожает тег и всё, что внутри;
  • extract() вырезает тег из дерева и возвращает его — если он ещё понадобится;
  • unwrap() убирает сам тег, но оставляет его содержимое: <b>текст</b> → текст.

Удалять удобно по списку, полученному заранее (select или find_all), — так вы не пропустите элементы, а вложенные друг в друга удалятся без ошибок.

Современные версии BeautifulSoup не включают код из <script> и <style> в результат get_text(), но в самом HTML (str(soup)) эти теги остаются. Если вы сохраняете очищенную страницу, удаляйте их явно.

Изменение

p = soup.find("p")
p.string = "Новый текст"               # заменить всё содержимое тега текстом
p["class"] = "lead"                    # изменить или добавить атрибут
del p["style"]                         # удалить атрибут
p.find("b").replace_with("вместо b")   # заменить тег строкой или другим тегом

Новый тег создают через сам объект soup и вставляют в нужное место:

link = soup.new_tag("a", href="/more")
link.string = "Подробнее"
p.append(link)                 # в конец тега
p.insert(0, "Внимание: ")      # в начало

Готовый HTML — str(soup), а с отступами для чтения — soup.prettify().

Регулярные выражения вместе с BeautifulSoup

BeautifulSoup отвечает за структуру (теги, атрибуты), а для поиска шаблонов в тексте — телефонов, адресов почты, дат — удобно модуль re:

import re

text = soup.get_text()
phones = re.findall(r"\+7\d{10}", text)

find_all тоже понимает регулярные выражения: soup.find_all(string=re.compile("скидка")) найдёт кусочки текста со словом «скидка», а soup.find_all(re.compile("^h[1-6]$")) — заголовки любого уровня.

Нормализация данных — обычная часть разбора: привести к нижнему регистру (.lower()), убрать повторы (set), отрезать лишнее (.split("?")[0]), отсортировать.

Порядок документа

find_all(["h1", "h2", "h3"]) возвращает теги в порядке документа, даже если перечислены разные виды. Это важно, когда смысл зависит от порядка: оглавление, пары «заголовок — текст». Если искать каждый вид отдельно и склеивать списки, порядок потеряется.

Итог

  • decompose(), extract(), unwrap() — удалить тег, вырезать, «развернуть»;
  • .string = ..., tag["атрибут"] = ..., replace_with, new_tag, append, insert — изменить дерево;
  • str(soup), prettify() — получить HTML;
  • re.findall по get_text() — шаблоны в тексте;
  • find_all([...]) сохраняет порядок документа.