Тестер CSS-селекторов

Проверяй CSS-селекторы на своём HTML онлайн — сколько элементов нашлось и какие именно

Селектор ничего не находит или находит сорок элементов вместо двенадцати, а в DevTools страница выглядит ровно так, как надо. Вставь тот HTML, который реально получает твой парсер, проверь на нём селекторы и посмотри все совпадения и их число до того, как запускать обход тысячи страниц.

HTML / XML

Нет входных данных


Как использовать

  1. 1Вставь HTML, который получает код, а не то, что показывает DevTools — Это два разных документа. Возьми тело ответа из Отправки запросов, или исходный код страницы, или Copy outerHTML, если точно знаешь, что фрагмент есть в исходнике. Проверка не на том документе — самая частая причина, по которой селектор проходит здесь и падает в бою.
  2. 2Напиши селектор — Работает всё, что понимает браузер: .product-card h2 a, div[data-id], ul.results > li:nth-child(2), nav a[href]:not([href='#']).
  3. 3Сначала смотри на число, потом на совпадения — Количество совпадений говорит больше, чем сами совпадения. Ждал двенадцать товаров, получил сорок — селектор выходит за пределы списка. Получил ноль — либо класса нет в сыром HTML вовсе, либо он написан иначе на один символ.
  4. 4Сужай, пока число не сойдётся — Добавь родителя, атрибут или структурный псевдокласс и следи, как меняется число. Совпадения пересчитываются по мере ввода; на большом HTML выключи автообработку и жми «Запустить».

Примеры

Селектор ничего не находит, хотя элемент на странице виден
.product-title -> 0 совпадений
Две обычные причины. Элемент добавляет JavaScript, и в сыром HTML его просто нет. Либо класс генерируется при сборке, и настоящий выглядит как ProductCard_title__x7Kd2. Найди во вставленном HTML слово из видимого текста и посмотри, во что оно обёрнуто на самом деле.
В DevTools работает, в парсере возвращает пусто
div.price
В консоли ты работаешь с деревом, которое уже собрали скрипты, а парсеру достаётся ответ до их запуска. Вставь сюда сырой ответ: если на нём селектор не срабатывает, дело в JavaScript, а не в селекторе.
Copy selector из DevTools ломается на второй странице
#root > div:nth-child(2) > div > div.sc-hKgILt > ul > li:nth-child(1) > a
Такой путь описывает вёрстку одной страницы, а не данные. Один лишний баннер — и все индексы съезжают. Замени его на самое короткое, что говорит о содержимом: [data-testid='product'] a или article.product a.
Имена классов меняются после каждого деплоя
[class^="ProductCard_"]
Styled-components, CSS-модули и подобное хешируют имена классов при сборке. Цепляйся за устойчивый префикс через ^= или, что надёжнее, за data-атрибут, id, itemprop либо структуру тегов — за всё, чего сборка не трогает.
Сорок совпадений вместо двенадцати
ul.results > li.item
Голый .item собирает весь документ, включая сайдбар и подвал. Добавь родителя и прямого потомка, а потом снова посмотри на число — это самая быстрая обратная связь, которую даёт инструмент.
Один и тот же класс в двух разных блоках
section.reviews .author
У отзывов и комментариев одинаковый .author. Ограничь селектор нужной секцией, вместо того чтобы потом отфильтровывать лишнее в коде.
Только настоящие ссылки, без мёртвых якорей
nav a[href]:not([href='#'])
В меню полно заглушек и отключённых пунктов. Отсеки их прямо в селекторе — список адресов получится чистым, и пропускать в коде будет нечего.
Каждая вторая строка или третья ячейка таблицы
tbody tr:nth-child(2n) td:nth-child(3)
Структурные псевдоклассы работают на статичном HTML полностью, так что полосатые таблицы и постраничные списки обходятся без дополнительной логики в коде.

Когда использовать

На CSS-селекторах работает большинство парсеров на Python и Node — BeautifulSoup, Scrapy, Cheerio и Playwright принимают их напрямую. Загвоздка в том, что селектор, проверенный в DevTools, проверен не на том документе. DevTools показывает DOM после выполнения скриптов, а парсер видит пришедшие байты. Всего, что вставил скрипт, там нет, и селектор, поймавший в консоли сорок элементов, в коде поймает ноль. Вторая постоянная проблема — имена классов, на которые нельзя опереться. Styled-components, CSS-модули и утилитарные фреймворки генерируют имена вида sc-hKgILt или ProductCard_title__x7Kd2, и те меняются при каждой пересборке сайта. Парсер, привязанный к ним, живёт неделю. Цепляйся за то, что не относится к оформлению: data-атрибут, id, itemprop, структуру тегов — или хотя бы за устойчивый префикс через [class^="ProductCard_"]. Инструмент работает с HTML как со строкой, ровно так же, как твой парсер. Он возвращает каждый найденный элемент и, что полезнее, их количество. Число и есть настоящая проверка: сорок штук вместо двенадцати означают, что селектор дотянулся дальше контейнера, а ноль — что искомого в этом HTML нет вовсе. Оба ответа приходят за секунды, а не после обхода. Чего инструмент не делает: он не выполняет JavaScript, а динамические псевдоклассы вроде :hover, :focus и :checked в статичном документе не значат ничего — структурные же (:nth-child, :first-of-type, :not()) работают полностью. Ещё CSS не умеет выбирать элемент по тексту и подниматься к родителю; когда нужно это, бери Тестер XPath. А чтобы получить сам сырой HTML, отправь запрос через Отправку запросов и вставь тело ответа сюда.

Частые вопросы

Ноль совпадений, а в браузере элемент есть. Что проверять первым?›
Найди в исходнике любое слово, которое видно на странице глазами. Если его там нет — содержимое строит JavaScript, и никакой селектор до него не дотянется, нужен инструмент с рендерингом вроде Playwright или Puppeteer. Если слово есть, посмотри, во что оно обёрнуто: класс почти всегда написан не так, как ты предполагал, чаще всего потому, что он генерируется при сборке.
Почему селектор работает в DevTools, но не в парсере?›
Они смотрят на два разных документа. Инспектор показывает живой DOM: скрипты уже отработали, а сломанную вёрстку браузер молча починил за автора. Парсеру достаётся сырой ответ, без того и другого. Забери этот ответ через Отправку запросов, вставь сюда и проверяй на нём — только такая проверка предсказывает, что сделает твой код.
Классы выглядят как sc-hKgILt и меняются после каждого деплоя. За что цепляться?›
Не за оформление. По убыванию надёжности: data-атрибуты ([data-testid="price"]), id, itemprop и другие смысловые атрибуты, дальше структура тегов (article > h2 > a) и только потом устойчивый префикс класса через [class^="ProductCard_"]. Хешированный хвост меняется при пересборке, а префикс обычно переживает её, потому что берётся из имени компонента.
Скопировал селектор из DevTools, а на следующей странице он сломался. Почему?›
Copy selector выдаёт путь вниз по одной конкретной странице, длинную цепочку из nth-child и хешированных классов. В нём зашита вёрстка именно этой страницы, поэтому один лишний баннер или один отсутствующий блок сдвигает все индексы. Пиши самый короткий селектор, который описывает данные, а не позицию, и проверь его здесь на двух разных страницах, прежде чем нести в код.
Как ограничить поиск одним блоком, если класс встречается дважды?›
Задай область через родителя: section.reviews .author вместо .author. Если блоки различаются только порядком, подойдёт :nth-of-type на родителе. Делать это в селекторе лучше, чем фильтровать потом в коде: число совпадений здесь сразу покажет, сработало ограничение или нет.
Как выбрать элемент сразу с несколькими классами?›
Пиши их подряд без пробелов: .btn.btn-primary найдёт элемент, у которого есть оба. Пробел означал бы потомка — это и есть обычная ошибка. Порядок не важен, и так же можно подмешивать атрибуты: a.btn[href].
Можно ли выбрать элемент по тексту?›
На CSS — нет. В спецификации нет селектора по тексту, а :contains() убрали из черновика много лет назад: библиотеки, которые его поддерживают, добавляют собственное расширение, и в браузере или здесь оно не сработает. Когда нужен выбор по тексту или подъём к родителю по содержимому, бери Тестер XPath.
Поддерживаются ли :hover и :focus?›
Нет, и не могут поддерживаться. Динамические псевдоклассы описывают состояние, которое существует только в живом браузере с человеком перед ним — в статичном документе наводить нечего. Структурные псевдоклассы — другое дело, они работают полностью: :nth-child, :nth-of-type, :first-of-type, :last-child, :not().
CSS-селекторы или XPath — что брать для парсинга?›
CSS для большей части работы: короче, читается легче, поддерживается всеми библиотеками. XPath — когда нужно то, чего CSS не выражает: выбор по тексту, подъём к родителю, условия на соседние элементы. На практике CSS закрывает большинство задач, а к XPath идёшь в тех немногих местах, где он сдаётся.
Работают ли эти селекторы в Scrapy и BeautifulSoup?›
Да. response.css() в Scrapy и select() в BeautifulSoup следуют спецификации CSS Selectors Level 4 — той же, которую реализует браузер здесь. Что проходит тут, пройдёт и там; отдельно стоит проверять разве что самые новые псевдоклассы.

Похожие инструменты