Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 4 из 25 ~20 мин +50 XP

Селекторы: как находить элементы

Селектор — половина успеха браузерного парсера: chromedp селекторы определяют, найдёт ли программа кнопку, цену или строку таблицы, и не упадёт ли на следующем редизайне. Разберём четыре стратегии поиска, работу с xpath go в chromedp, механику QueryAfter и практику стойких путей к элементам — на примерах, которые проверяются в консоли браузера за минуту.

Содержание урока · 10 разделов

Программа из прошлого урока отлично открывает страницу — и падает на первом же действии: узел по селектору так и не нашёлся, Run отработал дедлайн. В браузере элемент на глазах есть, в коде страницы тоже, а chromedp его не видит. Причин у этого три, и все три — про поиск. Либо селектор описывает не тот узел: путь скопирован из инспектора и привязан к порядку соседей, который изменился. Либо элемент существует, но невидим — скрыт CSS или лежит в закрытой вкладке, а действие требует видимости. Либо контент ещё не доехал: селектор правильный, но к моменту проверки узла в дереве нет. Поиск элементов — навык, который в браузерной автоматизации ценится выше запоминания функций: тот же самый Run с другим селектором работает и надёжен, а с плохим — рассыпается на каждом релизе целевого сайта. В этом уроке — система: какие режимы поиска есть у chromedp, чем они отличаются под капотом, как в него встраивается XPath и что делает селектор стойким к редизайнам.

chromedp селекторы: четыре пути к одному узлу#

У chromedp селекторы устроены интереснее, чем кажется: строка и режим поиска — это две разные вещи. Строка — то, что вы хотите найти: «h1.title», «#price», целое XPath-выражение. Режим — то, как браузер будет искать: он выбирается опциями BySearch, ByQuery, ByQueryAll, ByID и задаётся последним аргументом действия. По умолчанию действует BySearch — браузерная команда поиска, которая понимает три вида запросов сразу: CSS-селекторы, XPath-выражения и даже обычный текст. Поэтому WaitVisible("h1") без опций работает и как CSS, и, если начать строку с двойного слэша, как XPath. Для точного контроля есть точные режимы: ByQuery ищет через querySelector — один первый узел по CSS, ByQueryAll — через querySelectorAll — все узлы сразу, ByID — быстрый поиск по атрибуту id. Зачем точные режимы, если есть универсальный? Скорость и предсказуемость: querySelector и querySelectorAll — прямые браузерные команды с понятной семантикой, а универсальный поиск устроен иначе и для простых CSS-случаев избыточен. Правило выбора простое: есть id — берите ByID, обычная CSS-логика — ByQuery или ByQueryAll, текст и оси — XPath через дефолт.

ByQuery и ByID: точные режимы в действии

GO
var title, price string
err := chromedp.Run(ctx,
        chromedp.WaitVisible("h1.title", chromedp.ByQuery),
        chromedp.Text("h1.title", &title, chromedp.ByQuery),
        chromedp.WaitVisible("#price", chromedp.ByID),
        chromedp.Text("#price", &price, chromedp.ByID),
)

ByQuery прогоняет строку через document.querySelector — первый узел по CSS-селектору; ByID сначала дописывает решётку, если её нет, и ищет тем же querySelector по атрибуту id. Опция указывается последним аргументом каждого действия, поэтому в одном Run можно смешивать режимы под разные элементы: заголовок по CSS, цена по id — у каждого своя механика поиска.

CSS-селекторы: ByQuery и ByQueryAll#

CSS — рабочий язык поиска в большинстве задач, и хромедповские режимы отображаются на него один в один. ByQuery — это document.querySelector: вернуть первый узел, подходящий под селектор. ByQueryAll — document.querySelectorAll: вернуть коллекцию всех подходящих. Разница проявляется в поведении действий: Text с ByQueryAll прочитает текст всё равно первого узла — действие применяется к первому элементу результата, но условие готовности ослабляется: достаточно одного совпадения. Настоящее применение ByQueryAll — действия над списками: Nodes соберёт массив узлов для обхода, а опция AtLeast задаст минимальное число совпадений, ниже которого запрос считается не выполнившимся — полезно для строк таблицы, где «пусто» и «не доехало» надо различать. По умолчанию достаточно одного совпадения: пустой каталог и «строки ещё не доехали» для ByQueryAll выглядят одинаково, и именно AtLeast разделяет эти состояния — с AtLeast(10) запрос добросовестно будет ждать десятую строку. Сам язык CSS пересказывать здесь не будем: классы, атрибуты, комбинаторы и псевдоклассы — то же, что вы знаете по goquery из Colly, и если эта база проседает, лучший тренажёр — консоль браузера: document.querySelectorAll(".card") в DevTools отвечает на вопрос «что найдёт этот селектор» мгновенно и без запуска программы.

XPath в chromedp: xpath go на живой странице#

XPath — второй язык поиска, встроенный в chromedp через универсальный режим: строка, начинающаяся с двойного слэша, обрабатывается как XPath-выражение. Возможности, за которые xpath go ценят в парсерах, — это поиск по тексту и движение по дереву в обе стороны. CSS умеет искать вниз: от родителя к потомкам и соседям. XPath умеет и вверх: найти кнопку можно от текста внутри неё, а затем подняться к карточке parent::, в которой лежит цена. Самый частый паттерн практики — элементы без устойчивых классов, зато с надписью: //a[contains(text(), "Следующая")] находит кнопку пагинации по тексту, какой бы класс ей ни дали дизайнеры. chromedp селекторы с XPath пишутся той же строкой в тех же действиях — отдельного API нет, и это удобно: одно и то же действие Text принимает и CSS, и XPath. Платить приходится читаемостью: длинные оси и предикаты в коде труднее воспринимаются, чем CSS-селекторы, поэтому рабочая пропорция — CSS для структуры, XPath для текста и редких подъёмов к родителю. Проверять выражения проще всего там же, в консоли: document.evaluate отвечает за XPath в DevTools.

XPath: кнопка пагинации по тексту

GO
var next string
err := chromedp.Run(ctx,
        chromedp.WaitVisible("//a[contains(text(), 'Следующая')]"),
        chromedp.Text("//a[contains(text(), 'Следующая')]", &next),
)

Оба действия без опций работают в универсальном режиме: строка с двойным слэшем — это XPath-выражение, которое находит ссылку по тексту, а не по классу. Предикат contains(text(), ...) проверяет подстроку, поэтому селектор переживает вложенные span-ы и мелкие правки вёрстки кнопки; перед запуском программы выражение проверяется в консоли через document.evaluate — там вернётся тот же узел.

QueryAfter: что происходит между селектором и действием#

Каждое действие с селектором в chromedp — это конвейер, и полезно видеть его звенья. Сначала запрос ищет узлы выбранным режимом: поиска нет — документ переспрашивается с интервалом в несколько миллисекунд. Затем проверяются условия готовности: по умолчанию достаточно, чтобы узлы нашлись, опции NodeVisible, NodeEnabled и другие добавляют свои требования — они и превращают поиск в ожидание. Наконец, действие исполняется на найденных узлах: текст читается из первого, клик идёт по первому, значение ставится в первое. Хуком в этот конвейер служит QueryAfter — функция, которая выполняется после того, как селектор совпал и условия выполнены: в неё попадают контекст и найденные узлы, и там можно сделать всё, чего нет в готовых действиях — обойти список узлов, прочитать несколько атрибутов разом, собрать структуру. Половина «невозможных» задач решается на этом уровне без экзотики. Механику условий с поллингом мы разобрали в прошлый раз, а тонкая настройка — свои интервалы, свои предикаты — ждёт в уроке про умные ожидания; здесь важен сам принцип: селектор без условия — это поиск, селектор с условием — ожидание.

Стратегии поиска в chromedp#

РежимМеханика браузераКогда брать
BySearch (по умолчанию)DOM.performSearch: CSS, XPath или текстне думаем о режиме: строка сама скажет, что она такое
ByQuerydocument.querySelector — первый узел по CSSобычные задачи по CSS: один заголовок, одна кнопка
ByQueryAlldocument.querySelectorAll — все узлысписки и таблицы: Nodes, AtLeast, обход коллекции
ByIDпоиск по атрибуту idэлементы с id: самая короткая и быстрая дорога
ByJSPathJS-путь к элементу через Runtime.evaluateузлы вне обычного DOM, например внутри ShadowDOM

Стойкий селектор: анатомия#

Стойкость селектора — это его связь со стабильными вещами на сайте, а не с текущей модой вёрстки. Ранжирование надёжности в практике простое. Первый ранг — атрибуты, которые существуют для программ: id и data-атрибуты, особенно data-testid, которые тестировщики оставляют именно для автоматизации; ищутся они и обычным CSS — селектор вида [data-testid="add-to-cart"] читается как смысл и переживает любые редизайны вёрстки. Второй ранг — семантические классы и структурные теги с осмысленными именами: класс price или тег table — вещь, которую дизайнеры переименовывают неохотно. Третий ранг — комбинированные пути: стабильный родитель плюс простое правило внутри, вроде div.card span.price. За пределами рангов остаются позиции — nth-child и индексы в XPath-предикатах: они описывают не смысл, а случайный порядок. Длина пути — отдельный сигнал: чем короче путь, тем меньше у него точек отказа, поэтому селектор вида #price лучше цепочки из шести уровней. Последний штрих дисциплины — проверка на выборке: хороший селектор совпадает на первой странице каталога, на десятой и на карточке товара, а не только там, где вы его написали. Для строк таблиц с пагинацией эта проверка — обязательная, и в двадцать первом уроке она встраивается в сам сценарий скрапинга.

Чек-лист стойкого селектора#

  • есть id или data-атрибут — путь начинается с них, а не с позиции среди соседей
  • в пути нет nth-child и индексных предикатов, привязанных к случайному порядку
  • селектор читается как смысл («цена в карточке»), а не как маршрут по слоям вёрстки
  • длина минимальна: каждый дополнительный уровень — дополнительная точка отказа
  • проверен в консоли браузера на нескольких страницах, включая пустые и граничные случаи
  • режим поиска выбран осознанно: ByID для id, ByQuery для CSS, XPath для текста и осей

Оговорка про песочницу, как и обещали в начале курса: разбор селекторов здесь текстовый и интерактивный — запустить настоящую страницу из браузера песочница не может, для этого нужен локальный Go и установленный Chrome, а вся практика по поиску узлов выполняется в вашем DevTools и терминале. Зато навык переносится один в один: строка селектора, найденная в консоли, работает и в chromedp без изменений. Между делом обратите внимание, как много из этого урока вы уже умеете, если проходили Colly: CSS-язык тот же, изменился только исполнитель — вместо разбора ответа поиск ведётся в живом DOM.

Проверка: режим по умолчанию

Какой режим поиска chromedp использует без явных опций?

Проверка: XPath или CSS

В каком случае XPath оправдан перед CSS-селектором?

Что вернёт Text с ByQueryAll

Прочитайте код и выберите, что он выведет:

// <ul>
//   <li class="tag">go</li>
//   <li class="tag">chromedp</li>
//   <li class="tag">cdp</li>
// </ul>
// chromedp.Text(".tag", &s, chromedp.ByQueryAll)

Подберите режимы поиска

Восстановите опции: заголовок по CSS, цена по id, узлы карточек коллекцией.

var title, price string var cards []*cdp.Node err := chromedp.Run(ctx, chromedp.Text("h1", &title, chromedp.), chromedp.Text("#price", &price, chromedp.), chromedp.Nodes(".card", &cards, chromedp.), )

Расставьте чтение карточки товара

Соберите по порядку программу: контекст, переход и два чтения в разных режимах.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Карточки: стратегии поиска

Термин

BySearch

нажмите, чтобы перевернуть
Определение

Режим по умолчанию: универсальная браузерная команда поиска, понимающая CSS-селекторы, XPath и обычный текст.

1 / 6

Что дальше: находить элементы мы научились — пора забирать из них данные. В шестом уроке — извлечение текста и атрибутов: Text против TextContent, InnerHTML, значения атрибутов и разница между содержимым и видимым текстом. Пятый урок про клики и ввод — про действия над найденными узлами; селекторы из этого урока станут его первым аргументом. А если пойдёте дальше по курсу, вернётесь сюда: к девятому уроку — за поллингом по своим условиям, к двадцать первому — за таблицами и пагинацией, где стойкость селектора проверяется деньгами.

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • Селектор в chromedp — это строка плюс режим поиска: строка говорит «что», режим — «как искать».
  • По умолчанию работает BySearch: универсальный поиск понимает CSS, XPath и текст — потому WaitVisible("h1") работает без опций.
  • ByQuery — querySelector (первый узел), ByQueryAll — querySelectorAll (коллекция), ByID — самый короткий путь по id.
  • XPath входит через универсальный режим: поиск по тексту //a[contains(text(), «Далее»)] и оси вверх к родителю.
  • Действие применяется к первому узлу результата; коллекции обходят через Nodes, а AtLeast задаёт минимум совпадений.
  • QueryAfter — хук после совпадения селектора и условий: поллинг, условия NodeVisible, свои операции над узлами.
  • Стойкий селектор — от id и data-атрибутов, короткий, без позиций; проверяется в консоли браузера на нескольких страницах.

Домашнее задание

Откройте любой каталог с карточками товаров и соберите три селектора в консоли DevTools: цену, название и кнопку пагинации. Один из них сделайте через CSS, один — через id или data-атрибут, один — через XPath по тексту. Затем попробуйте сломать их: найдите вторую страницу каталога и проверьте, какие селекторы выжили. Выпишите победителей — они понадобятся в домашнем задании пятого урока, где по карточке нужно пройти кликом и вводом.

Личные заметкиПусто
Хранятся только в вашем браузере (Chromedp · Селекторы: как находить элементы)

Вопросы и ответы по уроку

Почему селектор из Copy selector в DevTools не работает надёжно?

Инспектор генерирует путь по позициям: nth-child(3), nth-child(2) — он описывает случайный порядок узлов, а не смысл. Любое изменение вёрстки сдвигает позиции, и селектор ломается. Стройте пути от id, data-атрибутов и семантических классов, а позиции оставьте как крайнюю меру.

Работает ли XPath в chromedp без специальных опций?

Да: универсальный режим поиска по умолчанию понимает XPath-выражения — строка с двойным слэшем обрабатывается как XPath. Специальной опции для этого не нужно; точные режимы ByQuery, ByQueryAll и ByID нужны для предсказуемой CSS-механики querySelector.

Чем отличается Text с ByQuery от Text с ByQueryAll?

Оба прочитают текст первого найденного узла, но с ByQueryAll условие готовности мягче: достаточно одного совпадения из возможных многих. Для чтения одного текста практичнее ByQuery; ByQueryAll имеет смысл для обхода списков через Nodes или QueryAfter. Практический ориентир: один конкретный элемент — ByQuery, коллекция для обхода — ByQueryAll с явным AtLeast.

Термины урока

Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.

Следующая ступень учебного пути

Testcontainers25 уроков~8 чпрактика25 уроков уже доступны

Testcontainers в Go: интеграционные тесты с Docker

Курс testcontainers-go: Postgres, Redis, Kafka и Mongo в тестах через Docker, wait-стратегии, сети, параллельность, CI и итоговый стенд. 25 уроков.

  • Поднимать реальные зависимости в Docker из кода теста
  • Использовать модули Postgres, Redis, MongoDB, Kafka и RabbitMQ
  • Ждать готовность сервиса wait-стратегиями вместо sleep
  • Собирать стенды из нескольких контейнеров и своего образа