Селектори CSS і XPath: приклади під типові сторінки магазину

6 хв

Селектор — це адреса значення на сторінці. Коли ви налаштовуєте парсинг сайту, ви не показуєте програмі колонку з ціною, як у файловому прайсі, а пишете, в якому місці розмітки ця ціна написана. Ця стаття — довідник таких адрес: що підтримує наш розбір, як виглядає робочий селектор під кожне поле товару й чому найчастіше не знаходиться нічого.

Порядок налаштування правил, вкладки, кнопки тесту й помічники описані окремо — у статті про парсинг сайтів. Тут лише мова селекторів і приклади.

Дві мови: CSS і XPath

Селектор пишуть однією з двох мов. За замовчуванням у комірці чекають CSS-селектор — той самий, яким верстальники описують стилі. Якщо зручніше XPath, у тому самому рядку правила ставлять галочку «XPath».

Сітка правил розбору джерела «Парсер сайту (приклади селекторів)»
1Селектор операції
2Позначка XPath
3Поле «Ціна»
  1. Колонка «Селектор операції» — сюди вписують адресу значення на сторінці.
  2. Позначка «XPath» у рядку — без неї XPath обриває розбір усієї сторінки, а не просто не знаходить значення.
  3. Рядок поля «Ціна» — у кожного поля свій селектор, налаштовуються вони незалежно.

Галочка не косметична. XPath, вписаний у рядок без неї, не «нічого не знаходить» — розбір цієї сторінки обривається з помилкою. У журналі при цьому буде код відповіді 200, тобто зовні все ніби добре, а даних немає. Так само обривається розбір на CSS-селекторі з синтаксичною помилкою: зайва дужка коштує цілої сторінки.

Різниця між «нічого не знайшов» і «помилка» важлива для пошуку причини. Селектор, який просто не знайшов елемента, дає порожній результат і йде далі — інші поля з тієї ж сторінки приїдуть нормально. Селектор із помилкою зупиняє всю сторінку.

Базовий набір: тег, клас, атрибут

Чотири способи вказати елемент, з яких складається майже будь-який робочий селектор:

ЗаписЩо вибирає
h1усі теги h1 на сторінці
.priceусе з класом price
#product-cardелемент із таким id
span[itemprop=price]span із таким атрибутом
div.card > aпосилання, яке лежить безпосередньо в div.card
div.card aпосилання будь-де всередині div.card, хоч на п'ятому рівні вкладеності

У квадратних дужках працює не лише точна рівність. Перед знаком рівності ставлять символ, який змінює правило порівняння:

ОператорУмоваПриклад
=значення точно такеa[rel=nofollow]
^=починається зa[href^=/product/]
$=закінчується наimg[src$=.jpg]
*=містить підрядокa[href*=catalog]
!=значення іншеinput[type!=hidden]
~=містить таке слово у списку через пробілdiv[class~=in-stock]
|=дорівнює або починається з «значення-»span[lang|=uk]

Оператор *= рятує на сайтах, де класи згенеровані складачем і виглядають як price__value_a7f3: div[class*=price__value] переживе зміну хвоста, а div.price__value_a7f3 — ні.

Повний перелік псевдокласів

Псевдоклас звужує вибірку за позицією або вмістом. Наш розбір підтримує рівно цей набір — усе інше, що можна побачити в довідниках CSS, тут не спрацює:

ПсевдокласЩо робить
:first-child, :last-childперший або останній серед сусідів
:nth-child(n), :nth-last-child(n)n-й за порядком, від початку або від кінця
:first-of-type, :last-of-type, :nth-of-type(n)те саме, але рахуються лише елементи того ж тега
:only-childєдина дитина в батька
:empty, :not-emptyпорожній / непорожній елемент
:contains(текст)містить текст
:has(селектор)всередині є елемент за селектором
:not(селектор)усе, крім того, що підходить під селектор
:checked, :disabled, :enabledстан елементів форми

У :contains() є два необов'язкові уточнення. Другий аргумент вмикає й вимикає врахування регістру, третій вимагає збігу цілком, а не входження:

  • td:contains(Гарантія) — містить це слово, регістр важливий;
  • td:contains(гарантія, false) — регістр не важливий;
  • td:contains(Гарантія, true, true) — текст комірки дорівнює саме цьому слову.

:contains() дивиться на власний текст елемента, а не на текст усіх нащадків разом. Якщо в розмітці <div><span>Гарантія</span></div>, то div:contains(Гарантія) не знайде нічого, а span:contains(Гарантія) знайде. Це найчастіша причина «псевдоклас не працює».

Забрати атрибут, а не текст

За замовчуванням із знайденого елемента береться його текст. Щоб узяти значення атрибута, до селектора дописують ::attr(ім'я). Без цього половина сайтів не збирається — ціна й наявність часто лежать не в тексті, а в розмітці для пошукових систем.

ЗавданняСелектор
Посилання на товарa.product-link::attr(href)
Ціна з мікророзміткиmeta[itemprop=price]::attr(content)
Фото з відкладеним завантаженнямimg.photo::attr(data-src)
Фото з набору розмірівimg.photo::attr(srcset)
Артикул із службового атрибутаdiv[data-sku]::attr(data-sku)

srcset обробляється по-різному на різних кроках. В операції «Картка товару» програма сама вибирає з набору посилання з найбільшою шириною — тобто повний розмір, а не прев'ю. У списку товарів такого відбору немає: там береться останнє посилання з набору. Якщо з лістингу приїжджають маленькі картинки, беріть інший атрибут — зазвичай поруч є data-src або src із повним розміром.

Рецепти під поля товару

Типові випадки, з яких складається більшість налаштувань. Класи в прикладах — вигадані, підставляйте свої; цінна тут форма запису.

Що требаЯк зазвичай написано в розмітціСелектор
Назва товаруєдиний h1 на сторінціh1
Назва в лістингутекст посилання на карткуdiv.catalog a.name
Цінаокремий блок із валютою поручdiv.price-current
Ціна в мікророзмітціатрибут, тексту немаєmeta[itemprop=price]::attr(content)
Стара ціна — щоб її НЕ взятилежить поруч із чинноюdiv.price:not(.price-old)
Артикулрядок «Код: 12345»span.sku + «Регулярний вираз» у тому ж рядку правила
Наявністьпідпис «Є в наявності»div.stock
Наявність за класомклас змінюється, тексту немаєdiv[class*=in-stock]
Усі фото галереїпосилання на повний розмірa[data-fancybox=gallery]::attr(href)
Тільки ті картки, де є картинкау списку трапляються рядки-заглушкиdiv.card:has(img) a::attr(href)
Значення характеристикитаблиця «назва — значення»tr:contains(Вага) td:last-child
Описблок із розміткою, яку треба зберегтиdiv.description + галочка «Отримувати HTML»
Посилання на другу сторінку розділунумерація внизуul.pagination a:not(.active)::attr(href)

Останній рядок таблиці з характеристикою варто прочитати уважно: tr:contains(Вага) td:last-child бере рядок таблиці, у якому є слово «Вага», і з нього — останню комірку. Це надійніше, ніж рахувати рядки за номером: у різних товарів набір характеристик різний, і tr:nth-child(4) у кожного другого товару вкаже не туди.

Коли потрібен XPath

CSS вистачає у більшості випадків, і починати краще з нього — він коротший і його простіше прочитати через півроку. XPath беруть тоді, коли треба піднятися вгору по дереву або перевірити умову, якої в CSS немає.

ЗавданняXPath
Взяти батька знайденого елемента//span[@class="sku"]/..
Комірка праворуч від комірки з текстом//td[text()="Вага"]/following-sibling::td[1]
Другий блок із класом(//div[@class="price"])[2]
Елемент за точним текстом//a[normalize-space(text())="Купити"]

Не забудьте про галочку «XPath» у тому ж рядку — без неї сторінка не розбереться взагалі.

Чому селектор нічого не знаходить

СимптомНайімовірніша причинаЩо зробити
Порожньо на всіх полях сторінкиу якомусь із правил синтаксична помилка або XPath без галочки — розбір обірвавсяперевірте всі правила операції, а не тільки те, яке «не працює»
Порожньо в одному поліселектор не знайшов елементаподивіться розмітку сторінки очима: клас міг бути з іншим написанням або взагалі згенерованим
Приїхала порожня назва, хоча на сайті вона єзначення промальовується скриптом уже в браузері, у вихідній розмітці його немаєувімкніть режим із виконанням скриптів у налаштуваннях джерела
Замість тексту — сміття з розміткиувімкнена галочка «Отримувати HTML»зніміть її або звузьте список дозволених тегів
Замість ціни приїхав нульселектор вказує на блок, де ціна написана словами або лежить в атрибутівізьміть ::attr(content) з мікророзмітки
Беруться всі фото разом із іконкамиселектор занадто широкий, наприклад просто imgзвузьте до контейнера галереї: div.gallery img::attr(src)
Селектор працював, а після оновлення сайту зламавсядовгий ланцюжок на кшталт div:nth-child(3) > div > span ламається від зайвого банераперепишіть коротше — за класом або атрибутом
Псевдоклас :contains() не спрацьовуєтекст лежить у вкладеному тегу, а не у власному тексті елементавкажіть той тег, у якому текст написаний безпосередньо

Часті питання

Звідки взяти селектор, якщо я не верстальник?

Найпростіше — відкрити сторінку в браузері, натиснути на потрібному значенні правою кнопкою й вибрати «Переглянути код». Браузер підсвітить рядок розмітки: у ньому видно тег, клас і атрибути, з яких складається селектор. У самій програмі поруч із правилами є ШІ-помічник: йому віддають шматок розмітки, він відповідає готовим селектором.

Браузер дає дуже довгий селектор. Його можна вставляти?

Технічно так, працюватиме. Практично — він проживе до першої перемальовки сайту: ланцюжок із :nth-child ламається навіть від зайвого банера в блоці. Візьміть із цього ланцюжка найближчий клас або атрибут і напишіть коротко.

Що буде, якщо селектор знайшов кілька елементів?

Вони склеюються через роздільник, заданий у тому ж рядку правила в колонці «Розділювач значення». За замовчуванням це кома, для категорій — скісна риска з пробілами, для фото — три вертикальні риски. Для полів, де потрібне одне значення, звужуйте селектор.

CSS чи XPath — що обрати?

Починайте з CSS: коротше й зрозуміліше. XPath потрібен, коли треба піднятися до батьківського елемента або взяти сусідню комірку таблиці за текстом — у CSS таких засобів немає.

Селектор бере «Код: 12345», а потрібні лише цифри. Як бути?

Це вже не завдання селектора. У тому самому рядку правила є колонка «Регулярний вираз» — вона витягає шматок зі знайденого значення. Поруч працюють «Знайти» / «Замінити» і «Текст для очищення», якщо достатньо простої заміни.

Як перевірити селектор, не запускаючи весь збір?

У рядку правила є кнопка тесту: вона відкриває сторінку, адресу якої ви вказали в колонці «Посилання для тестування», і показує, що повернув селектор. Порожня колонка з адресою — і кнопка просто нічого не зробить, без жодного повідомлення.

Суміжні теми

Чи була стаття корисною?