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

14 хв

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

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

Дві мови: 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(текст) або другий аргумент true), :contains() дивиться на власний текст елемента, а не на текст усіх нащадків разом. Якщо в розмітці <div><span>Гарантія</span></div>, то div:contains(Гарантія) не знайде нічого, а span:contains(Гарантія) знайде. Це найчастіша причина «псевдоклас не працює». З другим аргументом false перевіряється весь текст разом із вкладеними тегами, тож div:contains(гарантія, false) цей div знайде.

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

За замовчуванням із знайденого елемента береться його текст. Щоб узяти значення атрибута, до селектора дописують ::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() не спрацьовуєтекст лежить у вкладеному тегу, а не у власному тексті елементавкажіть той тег, у якому текст написаний безпосередньо

Сценарії

У рядку кожного поля є чотири колонки для селекторів — від «Селектор №1» до «Селектор №4». В операції «Картка товару» програма перебирає їх по порядку й зупиняється на першому, який дав значення. Виняток — фото: для нього застосовуються всі заповнені. Спершу програма шукає блок картки за селектором операції; якщо в рядку операції селектора немає, блоком вважається вся сторінка, а якщо блок не знайдено — поля картки не заповнюються. На цьому порядку тримаються всі сценарії нижче.

нітакнітактакнінітакнітакБлок за Селекторомопераціїзнайдено на сторінціПоля карткине заповнюютьсяУ рядку поля заповненохоч один Селектор№1–№4«Регулярний вираз», якщозаповнений,шукає по всьому кодусторінки,інакше селектор підбирає«Авто пошук селекторів»,якщо увімкненийЧерговий селектор попорядку№1 → №4, поки вони єСелектор у фігурнихдужкахПоле отримує текстз дужок як єПошук у блоці карткитекст або HTML,очищення,Регулярний виразЗначення порожнєабо ціна 0.00Значення записуєтьсяв полеПоле вже заповненеі це не фотоРешта селекторів поляне застосовуються
Ліва гілка — поле без селекторів: його заповнює «Регулярний вираз» по всьому коду сторінки, а якщо вираз порожній — лише «Авто пошук селекторів», коли він увімкнений. Права петля — перебір «Селектор №1»…«Селектор №4»: він обривається, щойно поле заповнилось, крім фото.

Ціна акційного товару лежить в іншому блоці

Задача: на звичайних товарах ціна в одному блоці, на акційних звичайного блоку немає, а ціна стоїть в іншому.

Що ввімкнути: у рядку «Ціна» операції «Картка товару» в «Селектор №1» — блок звичайної ціни, у «Селектор №2» — блок акційної.

Що вийде: там, де перший селектор знайшов ціну, вона й записується. Другий селектор застосовується тільки тоді, коли після першого поле лишилось порожнім. Блок, у якому немає жодної цифри, дає ціну 0.00 — таке значення не записується, і програма переходить до наступного селектора. Чого не станеться: дві ціни не склеяться. Якщо один селектор знайшов кілька цін, у поле піде перша ненульова з них.

Значення за замовчуванням, коли на сторінці його немає

Задача: у частини товарів постачальник пише статус наявності, а в решти блоку немає зовсім, і таким товарам потрібне одне й те саме значення.

Що ввімкнути: у рядку «Статус наявності (з прайсу)» операції «Картка товару» в «Селектор №1» — блок статусу, у «Селектор №2» — потрібний текст у фігурних дужках, наприклад {Під замовлення}.

Що вийде: текст у фігурних дужках — це не адреса на сторінці, а готове значення: програма бере його без дужок і нічого не шукає. Оскільки «Селектор №2» застосовується лише до порожнього поля, товари з блоком статусу отримають текст зі сторінки, а решта — Під замовлення. Чого не станеться: якщо поставити дужки в «Селектор №1», значення з сайту не забиратиметься зовсім — поле отримає цей текст у кожного товару, а наступні селектори рядка не застосовуються.

Артикул записаний як «Код: 12345»

Задача: в артикулі потрібні тільки цифри, а на сторінці поруч стоїть підпис.

Що ввімкнути: у рядку «Артикул контрагента» або «Артикул виробника» — селектор на блок з артикулом, наприклад span.sku.

Що вийде: у цих двох полях слова «Артикул», «Арт.», «Код товару», «Код», «SKU» і двокрапка вирізаються автоматично, регулярний вираз для цього не потрібен. Для іншого формату, наприклад коли артикул стоїть у дужках після назви, заповніть «Регулярний вираз» із круглими дужками навколо потрібного шматка: у поле піде вміст перших дужок першого збігу. Чого не станеться: якщо вираз нічого не знайшов, значення не стирається — у поле піде те, що повернув селектор.

Повний опис з абзацами й картинками

Задача: перенести опис товару з розбивкою на абзаци й списки, а не суцільним текстом.

Що ввімкнути: у рядку «Опис повний» — селектор на блок опису, галочку «Отримувати HTML» і в колонці «Дозволені HTML теги» — перелік тегів, які треба зберегти, наприклад <p><ul><li><img loading="lazy" alt="Селектори CSS і XPath приклади під типові сторінки магазину" title="Селектори CSS і XPath приклади під типові сторінки магазину">.

Що вийде: у поле піде розмітка блоку, у ній лишаться тільки перелічені теги. Відносні адреси картинок усередині опису доповнюються адресою сайту. Атрибут style і HTML-коментарі прибираються незалежно від налаштувань. Якщо селектор знайшов кілька блоків, вони з'єднуються пробілом, а не значенням із «Розділювач значення». Чого не станеться: без галочки «Отримувати HTML» і з порожньою колонкою «Дозволені HTML теги» усі теги видаляються й опис приїде одним текстом.

Фото з галереї і окремо головне фото

Задача: на сторінці головне фото лежить окремо від галереї, і потрібні обидва.

Що ввімкнути: у рядку фото (у колонці «Назва поля» він підписаний image) в «Селектор №1» — посилання галереї, наприклад div.gallery a::attr(href), у «Селектор №2» — головне фото, наприклад img.main::attr(src).

Що вийде: для фото програма не зупиняється на першому селекторі, що спрацював, а застосовує всі заповнені й збирає знайдене в один список. Адреси, у яких трапляється placeholder, no-photo, no-image, no_image, empty або /brand/, відкидаються поштучно — решта фото лишається. Чого не станеться: заглушка «немає фото» не стане головним фото товару.

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

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

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

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

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

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

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

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

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

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

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

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

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

Суміжні теми

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