Зачем нужны селекторы
После долгого использования метода .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
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.