Очистка и изменение 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([...])сохраняет порядок документа.