При автоматизации действий в браузере с помощью Playwright нам необходимо находить и выбирать элементы. Без возможности выбрать элемент мы не можем кликнуть по нему, заполнить форму, и даже прокрутить страницу к нужному элементу, потому что Playwright не знает, где он находится.
Если Playwright не может найти элементы на странице, он ничего не сможет сделать, кроме как открыть новые страницы и закрыть браузер.
В этой статье вы познакомитесь с базовыми и расширенными CSS-селекторами, а также научитесь применять их на практике.
- Как найти элементы с помощью CSS-селектора
- Понимание CSS-селекторов
- Базовые CSS-селекторы
- Расширенные CSS-селекторы
- Рекомендации
- Заключение
Подпишитесь на наш ТЕЛЕГРАМ КАНАЛ ПО АВТОМАТИЗАЦИИ ТЕСТИРОВАНИЯ
Как найти элементы с помощью CSS-селектора
Чтобы найти элементы по CSS-селектору в Node.js с помощью Playwright, следуйте приведенному ниже скрипту:
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com"); //find the first h1 element const h1 = page.locator("h1").first(); const h1Text = await h1.textContent(); console.log(`H1 element: ${h1Text}`); //find all elements with the class "quote" const quotes = page.locator(".quote"); //get the count of our quotes list const quoteCount = await quotes.count(); //iterate through quotes for (let i=0; i<quoteCount; i++) { //get the text of the quote text = await quotes.nth(i).textContent(); //log it to the console console.log(`Quote: ${text}`); } //close the browser await browser.close(); } main()
- Сначала мы открываем браузер с помощью функции
launch()
. - В браузере мы создаем новую вкладку с помощью browser.newPage() и переходим по URL-адресу Quotes to Scrape.
- Мы находим первый элемент
h1
на странице с помощьюpage.locator("h1").first()
. - После этого мы извлекаем текстовое содержимое найденного элемента
h1
с помощьюh1.textContent()
и выводим его в консоль. - Находим все элементы с классом “quote” (“цитата”) на странице, используя
page.locator(".quote")
. - Выводим текстовое содержимое каждой цитаты в консоль и закрываем экземпляр браузера, используя
browser.close()
, чтобы освободить системные ресурсы.
Понимание CSS-селекторов
CSS-селекторы используются не только для поиска элементов, но и для стилизации веб-страницы с помощью CSS. Зачастую при оформлении веб-страницы разработчик использует классы для придания стиля группе элементов.
Для начала давайте рассмотрим пример с HTML и CSS.
Создаём новый HTML-файл:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Our Demo Page</title> <link rel="stylesheet" type="text/css" href="demo.css"> </head> <body> <h1>Hello I am an HMTL File</h1> <p>These are some smaller words.</p> </body> </html>
Если вы откроете этот файл в браузере, он будет выглядеть довольно скучно:

Теперь перейдём к классу. Давайте создадим следующий CSS-файл:
.our-new-class { background-color: black; color: white; }
Далее мы обновим HTML-файл, добавив наш класс к тегу body
.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Our Demo Page</title> <link rel="stylesheet" type="text/css" href="demo.css"> </head> <body class="our-new-class"> <h1>Hello I am an HMTL File</h1> <p>These are some smaller words.</p> </body> </html>
Посмотрите, как теперь выглядит наш файл в браузере:

С помощью CSS-селекторов мы можем выбирать элементы на странице по:
- тегу
- id
- классу
- атрибуту
- потомку
- дочернему элементу
- соседнему элементу
- родственному элементу
- псевдоэлементу
- псевдоклассу
Тип селектора | Синтаксис CSS | Описание |
---|---|---|
Тег | tag | Выбирает элементы по их тегам. |
ID | #id | Выбирает элемент по его идентификатору. |
Класс | .class | Выбирает элементы по их классу. |
Атрибут | [attribute=value] | Выбирает элементы с определенным значением атрибута. |
Потомок | ancestor descendant | Выбирает элементы-потомки внутри другого элемента. |
Дочерний элемент | parent > child | Выбирает дочерние элементы указанного родителя. |
Соседний элемент | previous + next | Выбирает элемент, следующий непосредственно за другим. |
Родственный элемент | sibling ~ sibling | Выбирает все элементы, имеющие общего родителя и находящиеся на одном уровне. |
Псевдокласс | element:pseudo-class | Выбирает элементы в определенном состоянии. |
Базовые CSS-селекторы
Давайте рассмотрим некоторые базовые CSS-селекторы и их использование в Playwright.
Поиск по классу
Чтобы найти элемент по его классу, мы используем оператор .
перед именем этого класса.
Если, например, нужно найти класс с именем my-custom-class
, указываем в Playwright → .my-custom-class
.
В примере ниже показано, как найти элемент с классом tag
.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com"); //find the FIRST element with the class "tag" const firstTag = page.locator(".tag").first(); //get the text const text = await firstTag.textContent(); //log the text console.log("First tag:", text); //close the browser await browser.close(); } main();
В этом примере:
- Открываем браузер с помощью
playwright.chromium.launch()
- Создаем новую вкладку с помощью
browser.newPage()
- Переходим на веб-сайт, используя
page.goto()
- Находим первый элемент с классом
tag
с помощьюpage.locator(".tag").first()
- Получаем текст из тега, используя
firstTag.textContent()
- Выводим текст в консоль
console.log("First tag:", text)
- Закрываем браузер с помощью
browser.close()
Поиск по ID
Теперь найдем элемент, используя его ID.
Переходим на страницу входа в систему (“login”) и находим поле Username, используя его id с оператором #
.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/login"); //find the FIRST element with the id "username" const username = page.locator("#username").first(); //fill the box with text await username.fill("ScrapeOps"); //take a screenshot await page.screenshot({ path: "find-by-id.png" }); //close the browser await browser.close(); } main();
В этом примере:
- Открываем браузер с помощью
playwright.chromium.launch()
- Создаем новую вкладку, используя
browser.newPage()
- Переходим на сайт с помощью
page.goto()
- Находим поле Username по его ID с помощью
page.locator("#username").first()
- Заполняем поле текстом, используя
username.fill()
- Делаем скриншот с помощью
page.screenshot()
- Закрываем браузер, используя
browser.close()
Итак, поле Username найдено по его ID и успешно заполнено текстом, что подтверждает ниже представленный скриншот.

Поиск по тегу
Теперь давайте найдем элемент, используя имя его тега. Например, чтобы найти все элементы <h1>
, мы будем искать имя тега – h1
.
Пример ниже практически идентичен нашему первому примеру. Попробуйте найти отличия.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com"); //find the FIRST element with the tag "h1" const firstTag = page.locator("h1").first(); //get the text const text = await firstTag.textContent(); //log the text console.log("First h1 element by tag name:", text); //close the browser await browser.close(); } main();
В этом примере:
- Открываем браузер с помощью
playwright.chromium.launch()
- Создаем новую вкладку, используя
browser.newPage()
- Переходим на сайт с помощью
page.goto()
- Находим первый элемент
<h1>
, используя имя тега:page.locator("h1").first()
- Получаем его текст с помощью
firstTag.textContent()
- Затем мы выводим текст в консоль и закрываем браузер
Расширенные CSS-селекторы
Далее рассмотрены методы, сочетающие в себе базовые селекторы и операторы для более точной и эффективной фильтрации элементов.
Наличие атрибута
Приведенный ниже код находит все элементы, содержащие атрибут id
, и заполняет их текстом.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/login"); //find the all elements that have an ID attribute const itemsWithId = page.locator("[id]"); const itemsCount = await itemsWithId.count(); //fill the items with text for (let i=0; i < itemsCount; i++) { await itemsWithId.nth(i).fill("scrapeops"); } //take a screenshot await page.screenshot({ path: "find-by-attribute.png" }); //close the browser await browser.close(); } main();
В этом примере:
page.locator("[id]")
возвращает все элементы, имеющие атрибутid
- Наш результат – это не обычный массив. Чтобы перебрать его элементы, нам необходимо сначала получить их количество, используя
itemsWithId.count()
- Получив количество элементов, мы обращаемся к каждому из них и заполняем текстом:
itemsWithId.nth(i).fill("scrapeops")
- Далее мы делаем скриншот и закрываем браузер
Значение атрибута
В примере ниже показано, как найти все элементы с точным значением атрибута, а именно id = "username"
.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/login"); //find the all elements that have an ID attribute with the value "username" const itemsWithIdUsername = page.locator("[id='username']"); const itemsCount = await itemsWithIdUsername.count(); //fill the items with text for (let i=0; i < itemsCount; i++) { await itemsWithIdUsername.nth(i).fill("scrapeops") } //take a screenshot await page.screenshot({ path: "find-by-attribute-value.png" }); //close the browser await browser.close(); } main();
Единственное отличие в этом примере – использование page.locator("[id='username']")
вместо page.locator("[id]")
.
Содержимое атрибута
Далее мы найдем элементы по содержимому их атрибутов. Приведенный ниже код находит все элементы с атрибутом href
, содержащим слово “author”.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/"); //find the all elements with an href containing the word "author" const itemsWithAuthor = page.locator("[href*='author']"); const itemsCount = await itemsWithAuthor.count(); //log the items to the console for (let i=0; i < itemsCount; i++) { text = await itemsWithAuthor.nth(i).textContent() console.log("Text Content:", text); } //close the browser await browser.close(); } main();
В этом примере оператор *=
указывает, что значение атрибута содержит текст 'author'
.
Значение атрибута, начинающееся с определенной строки
Теперь мы найдем все элементы, у которых значение атрибута class
начинается с буквы “q”.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/"); //find the all elements with an class starting with "q" const itemsWithQuote = page.locator("[class^='q']"); const itemsCount = await itemsWithQuote.count(); //log the items to the console for (let i=0; i < itemsCount; i++) { text = await itemsWithQuote.nth(i).textContent() console.log("Text Content:", text); } //close the browser await browser.close(); } main();
Здесь оператор ^=
даёт команду нашему локатору искать только те элементы, у которых значение атрибута начинается с подстроки'q'
.
Значение атрибута, заканчивающееся на определенную строку
В этом примере мы найдем все элементы, у которых значение атрибута class
заканчивается на букву “e”.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/"); //find the all elements with an class ending with "e" const itemsWithQuote = page.locator("[class$='e']"); const itemsCount = await itemsWithQuote.count(); //log the items to the console for (let i=0; i < itemsCount; i++) { text = await itemsWithQuote.nth(i).textContent() console.log("Text Content:", text); } //close the browser await browser.close(); } main();
С помощью оператора $=
мы сообщаем Playwright, что нам нужны только те элементы, у которых значение атрибута заканчивается на символ “e”.
Селектор потомков
Приведенный ниже код находит все элементы div
, являющиеся потомками как минимум четырёх других элементов div
.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/"); //find the all div elements nested within at least four other divs const itemsFromDivs = page.locator("div div div div div"); const itemsCount = await itemsFromDivs.count(); //log the items to the console for (let i=0; i < itemsCount; i++) { text = await itemsFromDivs.nth(i).textContent() console.log("Text Content:", text); } //close the browser await browser.close(); } main();
В этом примере page.locator("div div div div div")
указывает Playwright найти все элементы div
, являющихся потомками не менее четырёх других элементов div
.
Дочерний селектор
Далее мы будем искать элементы с помощью оператора >
. Приведенный ниже код ищет все элементы div
, которые являются дочерними элементами тега body
.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/"); //find the all div elements that are direct children of the body element const divsFromBody = page.locator("body > div"); const itemsCount = await divsFromBody.count(); //log the items to the console for (let i=0; i < itemsCount; i++) { text = await divsFromBody.nth(i).textContent() console.log("Text Content:", text); } //close the browser await browser.close(); } main();
В этом примере:
page.locator("body > div")
сообщает Playwright, что нам нужны все элементыdiv
, которые являются дочерними элементами тегаbody
.- При поиске элементов таким способом всегда указывайте их в следующем порядке:
parentElement > childElement
Соседний селектор
Теперь для поиска мы будем использовать оператор +
. Приведенный ниже код ищет все элементы div, которые следуют за другим элементом div
и имеют общего родителя.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/"); //find the all div elements that are siblings adjacent to other divs const divsAdjacent = page.locator("div + div"); const itemsCount = await divsAdjacent.count(); //log the items to the console for (let i=0; i < itemsCount; i++) { text = await divsAdjacent.nth(i).textContent() console.log("Text Content:", text); } //close the browser await browser.close(); } main();
В этом примере page.locator("div + div")
сообщает Playwright, что нам нужны только те элементы div
, которые следуют непосредственно за другим элементом div
и имеют общего родителя.
Родственный селектор
Далее с помощью оператора ~
найдем все элементы div
, которые находятся на том же уровне вложенности, и расположены после указанного элемента.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/"); //find the all div elements that are siblings to other divs const divsGeneral = page.locator("div ~ div"); const itemsCount = await divsGeneral.count(); //log the items to the console for (let i=0; i < itemsCount; i++) { text = await divsGeneral.nth(i).textContent() console.log("Text Content:", text); } //close the browser await browser.close(); } main();
Здесь page.locator("div ~ div")
сообщает Playwright, что нам нужны все элементы div
, являющиеся последующими соседями другого элемента div
с тем же родителем.
Псевдоклассы и псевдоэлементы
В отличии от псевдоклассов, псевдоэлементы отсутствуют в DOM-дереве, поэтому их нельзя найти с помощью Playwright. Приведенный ниже код находит все элементы div
, которые являются первыми дочерними элементами своих родителей.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/"); //find all div elements that are first children of any element in the DOM const itemsList = page.locator("div:first-child"); const itemsCount = await itemsList.count(); //log the items to the console for (let i=0; i < itemsCount; i++) { text = await itemsList.nth(i).textContent() console.log("Text Content:", text); } //close the browser await browser.close(); } main();
Обратите внимание, что при использовании псевдокласса применяется следующий синтаксис → element:attribute-to-find
Комбинирование селекторов
И, наконец, мы найдем элементы, комбинируя несколько селекторов. Приведенный ниже код ищет все элементы div
, которые являются первыми дочерними элементами body
. При комбинировании нескольких селекторов мы просто передаем несколько селекторов в page.locator()
.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://quotes.toscrape.com/"); //find all div elements that are first children of the body element const itemsList = page.locator("body > div:first-child"); const itemsCount = await itemsList.count(); //log the items to the console for (let i=0; i < itemsCount; i++) { text = await itemsList.nth(i).textContent() console.log("Text Content:", text); } //close the browser await browser.close(); } main();
В этом примере page.locator("body > div:first-child")
указывает Playwright найти все элементы div
, которые являются первыми дочерними элементами body
.
Рекомендации
При работе с CSS-селекторами в Playwright следуйте следующим рекомендациям, чтобы избежать распространенных ошибок.
Используйте DevTools
Для того, чтобы просмотреть HTML-структуру страницы и подобрать правильный селектор используйте инструменты разработчика (DevTools). Для этого просто щелкните по странице правой кнопкой мышки и выберите пункт Inspect (“Просмотреть код”) в выпадающем меню.

Пишите поддерживаемые селекторы
Когда мы пишем селекторы, важно найти баланс между эффективностью и удобством сопровождения. При написании селекторов внутри скрипта Playwright старайтесь использовать селекторы, которые легко понять и поддерживать.
Всегда используйте комментарии, когда это необходимо. Они значительно облегчают чтение и поддержку кода.
Найдите баланс между конкретикой и гибкостью
Слишком специфические селекторы могут быть отличными… поначалу. Если вы пишете код для динамической страницы, может произойти ситуация, когда селектор не найдет элемент, найденный вчера.
Чтобы избежать этого, пишите устойчивые селекторы, которые не будут ломаться при незначительных изменениях в HTML-структуре.
Не выбирайте элементы без причины
Выполняемый код потребляет ресурсы, и селекторы не являются исключением. Выбирайте только необходимые элементы на странице, иначе Playwright будет работать медленно или вовсе зависнет.
Используйте ожидания динамических элементов
Прежде чем взаимодействовать с динамическими элементами, необходимо дождаться их появления на странице. Для этого можно использовать различные методы ожидания в Playwright.
- Фиксированное ожидание – В примере ниже, используя
page.waitForTimeout(1000)
, мы ждем ровно одну секунду, а затем делаем скриншот страницы.
const playwright = require("playwright"); async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://www.espn.com/"); //wait one second await page.waitForTimeout(1000) //take a screenshot await page.screenshot({ path: "hardcoded-wait.png"}); //close the browser await browser.close(); } main();
- Сетевое ожидание – Приведенный ниже код выполняет ожидание на основе состояния сети. Мы используем
page.waitForLoadState()
и передаем ему в качестве параметра"networkidle"
. Это указывает Playwright на необходимость подождать, пока сеть не перейдет в режим ожидания, прежде чем продолжить выполнение скрипта.
async function main() { //open a browser const browser = await playwright.chromium.launch(); //open a new page const page = await browser.newPage(); //navigate to the site await page.goto("https://www.espn.com/"); //wait until the network is idle await page.waitForLoadState("networkidle") //take a screenshot await page.screenshot({ path: "network-wait.png"}); //close the browser await browser.close(); } main();
Заключение
Итак, теперь вы хорошо разбираетесь в основах Playwright и CSS-селекторах. Вы определенно сможете воспользоваться этими знаниями и создать свой первый веб-скрапер с помощью Playwright.
Перевод статьи «Playwright Guide: How To Find Elements by CSS Selector».