CSS-селекторы и навигация по дереву
CSS-селекторы — компактный язык адресации элементов, знакомый по вёрстке.
Методselectв BeautifulSoup принимает CSS-селектор:div.product > span.price. Это часто короче и выразительнее, чем цепочкиfind.
Если ты знаком с CSS, селекторы покажутся естественными. .price — элемент с классом price, #main — с id main, div a — все ссылки внутри div, ul > li — прямые потомки. BeautifulSoup поддерживает их через select (все) и select_one (первый).
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
# все цены
prices = soup.select('div.product span.price')
# первый заголовок внутри карточки
title = soup.select_one('.product .title')
# ссылки только прямых потомков списка
items = soup.select('ul.menu > li > a')Навигация по дереву
Найдя элемент, можно ходить по соседям и родителям — это спасает, когда у нужного значения нет своего класса, но рядом есть «якорь».
row = soup.find('td', string='Цена')
value = row.find_next_sibling('td').text # соседняя ячейка
card = price_tag.find_parent('div', class_='product')
title = card.find('h2').textКак работает под капотом
BeautifulSoup транслирует CSS-селектор в обход дерева через библиотеку soupsieve. Комбинатор > означает «прямой потомок», пробел — «любой потомок на любой глубине». Навигационные методы (find_parent, find_next_sibling) двигаются по связям узлов, не делая повторного поиска по всему документу, — это быстро и предсказуемо. Извлечение списка значений — типовой приём:
Попробуй сам ▶
# демонстрация логики «собрать тексты» на чистом Python
rows = [
{'title': 'Python с нуля', 'price': '0'},
{'title': 'SQL продвинутый', 'price': '1990'},
{'title': 'Скрейпинг', 'price': '2490'},
]
# так выглядит результат после .select(...) -> список словарей
total = 0
for r in rows:
print(f"{r['title']:20} {r['price']:>6} руб")
total += int(r['price'])
print('-' * 30)
print(f"{'Итого':20} {total:>6} руб")find или select: что выбрать
BeautifulSoup даёт два стиля поиска, и оба легитимны. Методы find/find_all — «питоний» способ: ищешь по имени тега и именованным аргументам (class_, id, attrs). Методы select/select_one принимают CSS-селектор строкой. CSS часто короче для вложенных условий: div.product a.buy против цепочки из нескольких find. На практике их свободно смешивают: нашёл карточку через select_one, а внутри неё дёргаешь поля через find.
Отдельно стоит освоить регистрозависимость и комбинаторы. Пробел между селекторами означает «потомок на любой глубине», > — «прямой ребёнок», , — «или то, или другое». Псевдоклассы вроде :nth-of-type мощны, но делают селектор хрупким — их применяют осторожно. Хорошая привычка — сначала подобрать селектор прямо в консоли браузера через document.querySelectorAll('...'), убедиться, что он ловит ровно нужные элементы, и только потом переносить его в код. Так ты экономишь время на отладке и не гоняешь лишние запросы к сайту ради проверки.
Частые ошибки
- Слишком хрупкие селекторы.
div > div > div:nth-child(3)ломается от любой перестановки. Цепляйся за классы и осмысленные атрибуты. - Путать
selectиfind.selectвсегда возвращает список (даже из одного элемента), аfind— один элемент. - Забывать про точку перед классом.
select('product')ищет тег, аselect('.product')— класс.
Best practices
- Предпочитай селекторы по классу и атрибутам, а не по позиции (
nth-child). - Используй навигацию по соседям, когда у значения нет своего класса, но есть подпись рядом.
- Проверяй селектор прямо в консоли браузера через
document.querySelectorAll.
Помни о компромиссе между точностью и устойчивостью селектора. Слишком общий селектор (div) поймает лишнее; слишком конкретный и завязанный на позицию (div:nth-child(4) > span) развалится при малейшем изменении вёрстки. Золотая середина — цепляться за смысловые признаки: семантические классы, id, атрибуты data-* и текстовые «якоря» рядом с нужным значением. Хороший селектор переживает мелкие правки дизайна и ломается только при действительно серьёзных изменениях структуры страницы.
На практике селекторы удобно подбирать итеративно: открыл инспектор, скопировал предлагаемый браузером селектор, упростил его до самой устойчивой части, проверил через document.querySelectorAll, что он ловит ровно нужные элементы и ничего лишнего, и только потом перенёс в код BeautifulSoup. Этот цикл «инспектор → упрощение → проверка → код» превращает подбор селектора из гадания в инженерную процедуру и резко снижает число правок, когда скрейпер запускается на полном объёме страниц.
Итог: CSS-селекторы делают код короче и читаемее. select + навигация по дереву (find_parent, find_next_sibling) покрывают практически любой случай извлечения.