Зачем нужны селекторы

После долгого использования метода .find() ваш код рано или поздно превращается в кашу. Например, вот код парсера для интернет-магазина:

product_cards = soup.find('main').find('table', class_='MainTable').find_all('div', class_='product-card')
for product_card in product_cards:
    product_image = product_card.find('img')
    ...

Код ищет карточки товаров по сложной комбинации из тегов и классов, потом в цикле снова начинаем погружаться в теги и классы, чтобы достать картинки товаров. Такой код совершенно нечитаемый и неудобный, без дополнительных комментариев невозможно понять, что тут происходит.

Это можно исправить, если использовать селекторы.

Кто такие селекторы

Селекторы — это правила, по которым можно найти элемент в HTML. По-сути, они и написаны в коде выше, только с помощью методов .find(). Например, возьмём код с несколькими .find():

soup.find('main') \
    .find('table', class_='MainTable') \
    .find_all('div', class_='product-card')

Этот код в виде селектора выглядит так:

main table.MainTable div.product-card

Чтобы создать селектор, достаточно перечислить теги, так же, как в .find(). Если вы использовали поиск по классам, то они пишутся через точку после тега.

А вот как использовать селектор:

selector = "main table.MainTable div.product-card"
product_cards = soup.select(selector)

Код стал заметно чище и проще. Сравните:

product_cards = soup.find('main').find('table', class_='MainTable').find_all('div', class_='product-card')
for product_card in product_cards:
    product_image = product_card.find('img')

# vs

selector = "main table.MainTable div.product-card"
product_cards = soup.select(selector)
for product_card in product_cards:
    product_image = product_card.find('img')

Но это ещё не всё. Селекторы сильнее, чем комбинация нескольких .find(). Например, чтобы выбрать все картинки из карточек товаров, достаточно добавить к селектору img. Т.е. теперь можно ещё и от цикла избавиться:

product_cards = soup.find('main').find('table', class_='MainTable').find_all('div', class_='product-card')
for product_card in product_cards:
    product_image = product_card.find('img')

# vs

images_selector = "main table.MainTable div.product-card img"
product_images = soup.select(images_selector)

Метод .select() сам возьмёт все карточки книг и найдёт в них все картинки, которые есть внутри. Больше для этого не нужно писать циклы.

Более того, можно сделать селекторы ещё и стабильнее. Вёрстка сайта может измениться: один <div> заменили на <span>, другой <div> стал семантичным <article>, третий <div.container> совсем исчез. Если перечислить все теги на пути от <body> до самого нижнего <img>, то любое изменение в верстке убъет парсер. Нам придётся его чинить, его работа будет неустойчивой.

Теги могут заменять один на другой, а классы обычно оставляют на местах. Можно убрать из селектора теги, к которым привязаны классы:

main table.MainTable div.product-card img

main .MainTable .product-card img

Эти два селектора сработают одинаково.

.select_one()

Метод .find_all() возвращает все подходящие элементы, так же, как и метод .select().

У него есть брат-близнец, который возвращает только один, первый подходящий элемент: .find(). У .select() тоже есть такой брат-близнец: .select_one(). Он возвращает только первый найденный по селектору элемент.

Что читать


Попробуйте бесплатные уроки по Python

Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.