Puppeteer — это библиотека для Node.js, предназначенная для
автоматизации браузера через API Chrome или Chromium. Она предоставляет
мощные средства для манипуляций с DOM, тестирования и веб-скрапинга.
Важной частью работы с Puppeteer является выбор элементов на странице.
Стандартные селекторы, такие как page.$(selector)</code>, дают
возможность находить элементы по CSS-селекторам, однако для сложных
страниц или специфических задач часто требуется создание кастомных
селекторов.</p>
<h3 id="зачем-нужны-кастомные-селекторы">Зачем нужны кастомные
селекторы</h3>
<p>Стандартные CSS-селекторы часто не могут покрыть все сценарии,
встречающиеся при тестировании динамичных веб-страниц. Это может быть
связано с:</p>
<ul>
<li><strong>Динамическими изменениями
страницы</strong>: элементы,
которые могут быть перерисованы или изменены на лету.</li>
<li><strong>Уникальными атрибутами</strong>: например,
отсутствие
уникальных id или классов.</li>
<li><strong>Необходимостью работы с состоянием
элементов</strong>:
например, нужно выбрать элемент только тогда, когда он доступен для
взаимодействия.</li>
</ul>
<p>Для таких случаев могут быть полезны кастомные селекторы,
которые
могут учитывать дополнительные параметры, такие как состояние элемента,
его видимость, расположение или конкретное поведение на
странице.</p>
<h3
id="создание-кастомных-селекторов-с-помощью-функции-page.eval">Создание
кастомных селекторов с помощью функции
<code>page.$eval
Одним из способов создания кастомного селектора в Puppeteer является
использование метода page.$eval</code>. Этот метод позволяет
выполнить произвольный JavaScript-код в контексте страницы, что дает
возможность динамически вычислять или изменять селектор в зависимости от
состояния страницы.</p>
<pre class="javascript"><code>const element = await
page.$eval(& // Логика для поиска нужного элемента return
el.querySelector('.my-class'); });
В этом примере используется стандартный CSS-селектор для поиска первого
элемента div. Однако, внутри функции обработчика, можно
добавить любую логику, например, вычисление или выбор более
специфического элемента.
На сложных веб-страницах элементы могут изменяться в зависимости от состояния интерфейса. Примером такого элемента является список, который подгружается асинхронно. В этом случае нужно создать кастомный селектор, который будет ожидать появления нужного элемента в DOM.
const customSelector = async (page, className) => {
const selector = `.${className}`;
await page.waitForSelector(selector, { visible: true });
return await page.$(selector);
};
Этот кастомный селектор использует waitForSelector, чтобы
дождаться появления элемента с заданным классом. Таким образом, если
элемент появляется динамически, Puppeteer подождет его загрузки, прежде
чем продолжить выполнение.
Многие элементы на странице могут быть скрытыми, отключенными или находиться в состоянии загрузки. Использование кастомных селекторов дает возможность проверять эти состояния до выполнения действий с элементами.
Для этого можно использовать методы isIntersectingViewport
и isVisible. Они позволяют оценивать, виден ли элемент на
экране и готов ли он к взаимодействию.
const customSelector = async (page, className) => {
const element = await page.$(`.${className}`);
const isVisible = await page.evaluate((el) => {
const rect = el.getBoundingClientRect();
return rect.top >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight);
}, element);
if (!isVisible) {
throw new Error(`Element .${className} is not visible.`);
}
return element;
};
В этом примере кастомный селектор проверяет, виден ли элемент на экране. Если элемент не видим, он выбрасывает ошибку.
В Puppeteer можно использовать не только CSS-селекторы, но и XPath, что значительно расширяет возможности поиска элементов, особенно когда требуется точность или выборка элементов с конкретными аттрибутами.
const customXPathSelector = async (page, xpathExpression) => {
const [element] = await page.$x(xpathExpression);
if (element) {
return element;
} else {
throw new Error(`Element matching ${xpathExpression} not found`);
}
};
В этом примере функция использует метод page.$x</code>, который
позволяет искать элементы с помощью XPath. Это может быть полезно, если
структура страницы сложная и требует более точного выбора элементов по
тексту, аттрибутам или относительному расположению.</p>
<h3 id="оптимизация-кастомных-селекторов">Оптимизация кастомных
селекторов</h3>
<p>Для повышения эффективности кастомных селекторов стоит
учитывать
несколько рекомендаций:</p>
<ol type="1">
<li><p><strong>Использование
<code>waitForSelector</code> с
таймаутами</strong>: Это помогает избежать ситуаций, когда скрипт
не
может найти элемент из-за его задержки в рендеринге.</p>
<pre class="javascript"><code>await
page.waitForSelector('.my-element', { timeout: 5000
});</code></pre></li>
<li><p><strong>Параллельные запросы</strong>:
Когда необходимо искать
несколько элементов, можно использовать параллельное выполнение запросов
для ускорения работы.</p>
<pre class="javascript"><code>const elements = await
Promise.all([
page.$('.first-class'), page.$('.second-class')
]);</code></pre></li>
<li><p><strong>Минимизация количества вызовов к
странице</strong>: Чем
меньше раз вы обращаетесь к странице для поиска элементов, тем быстрее
будет работать тест. Поэтому лучше собрать все нужные элементы за один
запрос и затем работать с ними.</p></li>
<li><p><strong>Использование прямых
ссылок</strong>: Вместо того чтобы
каждый раз искать элемент через стандартный CSS-селектор, можно
использовать ссылки на элементы, если они известны.</p></li>
</ol>
<h3 id="примеры-сложных-кастомных-селекторов">Примеры сложных
кастомных
селекторов</h3>
<p>Иногда требуется делать сложные выборки на основе нескольких
условий,
таких как родительский элемент, тип контента или конкретные атрибуты. В
таких случаях можно комбинировать различные подходы для создания
эффективных кастомных селекторов.</p>
<p>Пример:</p>
<pre class="javascript"><code>const complexSelector = async
(page) => {
const element = await page.$eval('.parent-element', (parent)
=> { return
parent.querySelector('.child-element[data-status="active"]'); });
if (!element) { throw new Error('Active child element not found'); }
return element; };В этом примере используется комбинация стандартного CSS-селектора и логики на стороне клиента, чтобы найти элемент с заданными аттрибутами в родительском контейнере.
Создание кастомных селекторов в Puppeteer позволяет значительно повысить гибкость и точность автоматизированных тестов. Они могут быть полезны для работы с динамическими элементами, сложными структурами DOM и улучшением производительности при тестировании веб-страниц. Комбинируя стандартные методы с пользовательскими решениями, можно добиться высокой надежности и скорости выполнения тестов.