Автоматизируйте. Меняйте.
Развивайте.
[email protected]
[email protected]
t.me/nodul
Форум Nodul
Готовые автоматизации
Партнерам
Вакансии
+569-231-213
page.evaluate() — это ключевой метод Puppeteer, который позволяет выполнять JavaScript напрямую в контексте браузера. Он служит мостом между Node.js и браузером, обеспечивая возможности для манипуляции DOM, извлечения данных и автоматизации динамических веб-страниц. Вот что нужно знать:
const title = await page.evaluate(() => document.title);
Этот код получает заголовок страницы напрямую из браузера.
page.evaluate
При работе с Puppeteer для автоматизации веб-страниц важно понимать разницу между контекстом Node.js и контекстом браузера. Эти две среды изолированы друг от друга, каждая со своими правилами выполнения кода и обмена данными.
Puppeteer работает в двух средах: контекст Node.js, где выполняется основной скрипт, и контекст браузера, где происходят взаимодействия с веб-страницей. Это отдельные процессы, каждый со своей виртуальной машиной.
Вот краткое сравнение их ключевых характеристик:
Обмен данными между этими контекстами включает несколько шагов, сильно зависящих от сериализации:
Ключевые ограничения: функции в контексте браузера не могут напрямую обращаться к переменным из контекста Node.js. Puppeteer предлагает специальные инструменты для решения этих задач:
Однако JSON-сериализация может удалить некоторые свойства, особенно у сложных объектов, таких как DOM-узлы. Чтобы избежать проблем, передавайте данные как аргументы функции, а не полагайтесь на переменные Node.js.
Освоение этих техник обмена данными гарантирует эффективное использование page.evaluate() для задач автоматизации. Далее рассмотрим практические примеры.
Синтаксис:
await page.evaluate(pageFunction, ...args);
Примеры:
const headingText = await page.evaluate(() => {
return document.querySelector('h1').textContent;
});
await page.evaluate((username, password) => {
document.getElementById('username').value = username;
document.getElementById('password').value = password;
document.querySelector('#login-form').submit();
}, 'myUsername', 'myPassword');
await page.evaluate(() => {
const div = document.createElement('div');
div.textContent = 'Добавлено с помощью Puppeteer';
document.body.appendChild(div);
return div.textContent;
});
Совет по отладке: используйте следующую конфигурацию для включения отладки во время разработки:
const browser = await puppeteer.launch({
headless: false,
slowMo: 100 // Добавляет задержку в 100 мс для каждой операции
});
Далее рассмотрим техники обмена данными между контекстами Node.js и браузера.
При передаче данных с page.evaluate используйте JSON-сериализуемые значения для входных аргументов.
Вот краткий обзор поддерживаемых типов параметров:
Теперь рассмотрим, как эти значения возвращаются из контекста браузера.
При использовании page.evaluate возвращаемые значения автоматически сериализуются в JSON. Вот как это работает:
// Возврат простого значения
const pageTitle = await page.evaluate(() => document.title);
// Возврат сложного объекта
const metrics = await page.evaluate(() => ({
viewport: window.innerWidth,
scrollHeight: document.body.scrollHeight,
timestamp: Date.now()
}));
"Как правило, если возвращаемое значение функции сложнее, чем JSON-объект (например, большинство классов), то evaluate, скорее всего, вернет усеченное значение (или {}). Это происходит потому, что мы возвращаем не фактическое значение, а десериализованную версию в результате передачи через протокол Puppeteer."
После получения вывода могут возникнуть проблемы, связанные с сериализацией. Вот как их решить.
Некоторые сценарии требуют специальных решений:
const bodyHandle = await page.$('body');
const html = await page.evaluate(body => body.innerHTML, bodyHandle);
await bodyHandle.dispose(); // Всегда очищайте, чтобы избежать утечек памяти
await page.exposeFunction('md5', text =>
crypto.createHash('md5').update(text).digest('hex')
);
const hash = await page.evaluate(async () => {
return await window.md5('test-string');
});
Если вы работаете с TypeScript, убедитесь, что транспилятор настроен правильно:
// tsconfig.json
{
"compilerOptions": {
"target": "es2018"
}
}
Эти стратегии помогут эффективно обмениваться данными в различных контекстах.
Вот как можно использовать page.evaluate в реальных сценариях с практическими примерами кода.
Пример: сбор деталей товара
Этот скрипт собирает информацию о названии, цене, рейтинге и наличии товара на веб-странице:
const productData = await page.evaluate(() => {
const products = Array.from(document.querySelectorAll('.product-card'));
return products.map(product => ({
title: product.querySelector('.title').textContent.trim(),
price: product.querySelector('.price').textContent.trim(),
rating: parseFloat(product.querySelector('.rating').dataset.value),
inStock: product.querySelector('.stock').textContent.includes('В наличии')
}));
});
Пример: извлечение данных из таблицы
Этот подход извлекает данные из таблицы, перебирая её строки и столбцы:
const tableData = await page.evaluate(() => {
const rows = Array.from(document.querySelectorAll('table tr'));
return rows.map(row => {
const columns = row.querySelectorAll('td');
return Array.from(columns, column => column.innerText);
});
});
Базовая автоматизация форм
Вот как заполнить поля формы, вызвать события и отправить форму:
await page.evaluate(() => {
// Заполнение полей формы
document.querySelector('#username').value = 'testuser';
document.querySelector('#password').value = 'secretpass';
// Вызов событий для динамических форм
const event = new Event('input', { bubbles: true });
document.querySelector('#username').dispatchEvent(event);
// Отправка формы
document.querySelector('form').submit();
});
Работа со сложными формами
Для задач, таких как выбор опций из выпадающего списка или отметка радио-кнопок:
await page.evaluate(() => {
// Выбор опции из выпадающего списка
const select = document.querySelector('#country');
select.value = 'US';
select.dispatchEvent(new Event('change', { bubbles: true }));
// Отметка радио-кнопки
const radio = document.querySelector('input[value="express"]');
radio.checked = true;
radio.dispatchEvent(new Event('change', { bubbles: true }));
});
Пример: бесконечная прокрутка
Этот скрипт прокручивает страницу, пока не соберет хотя бы 100 элементов:
const items = await page.evaluate(async () => {
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const items = new Set();
while (items.size < 100) {
// Прокрутка вниз
window.scrollTo(0, document.body.scrollHeight);
// Ожидание нового контента
await delay(1000);
// Сбор элементов
document.querySelectorAll('.item').forEach(item =>
items.add(item.textContent.trim())
);
}
return Array.from(items);
});
Пример: обработка AJAX-контента
Для динамической загрузки контента этот скрипт нажимает кнопку "Загрузить ещё" и ожидает появления новых элементов:
await page.evaluate(async () => {
// Нажатие кнопки "Загрузить ещё"
document.querySelector('#loadMore').click();
// Ожидание обновления контента
await new Promise(resolve => {
const observer = new MutationObserver((mutations, obs) => {
if (document.querySelectorAll('.item').length > 10) {
obs.disconnect();
resolve();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
});
Эти примеры демонстрируют, как обрабатывать различные сценарии, такие как сбор данных, автоматизация форм и динамический контент. Настройки можно адаптировать под конкретную структуру и поведение веб-страницы.
Нодуль интегрирует основные функции Puppeteer в свои рабочие процессы автоматизации, упрощая выполнение JavaScript напрямую в браузере. С page.evaluate пользователи могут манипулировать DOM и извлекать данные эффективно. Этот подход обеспечивает плавную интеграцию сложной обработки данных и операций с DOM в среде автоматизации Нодуля.
Модуль автоматизации браузера в Нодуле использует page.evaluate для выполнения задач от простых операций с DOM до сложных сценариев JavaScript. Вот как это работает:
// Базовое взаимодействие с DOM
await page.evaluate(() => {
const loginButton = document.querySelector('#login');
loginButton.click();
// Вызов пользовательского события
loginButton.dispatchEvent(new Event('customClick'));
});
// Обработка данных с помощью открытых функций
await page.exposeFunction('processData', async (data) => {
// Обработка данных в контексте Node.js
return transformedData;
});
await page.evaluate(async () => {
const rawData = document.querySelector('#data').textContent;
const processed = await window.processData(rawData);
return processed;
});
Нодуль также ведет журнал выполнения, что упрощает отладку скриптов.
Нодуль хорошо подходит для обработки динамического контента и сложных задач автоматизации. Вот пример обработки динамического контента на странице:
const extractProductData = await page.evaluate(async () => {
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
// Ожидание загрузки динамического контента
while (!document.querySelector('.product-grid')) {
await delay(100);
}
return Array.from(document.querySelectorAll('.product'))
.map(product => ({
name: product.querySelector('.name').textContent,
price: product.querySelector('.price').textContent,
availability: product.querySelector('.stock').dataset.status
}));
});
Для более сложных операций page.exposeFunction позволяет легко взаимодействовать между Node.js и браузером:
await page.exposeFunction('md5', text =>
crypto.createHash('md5').update(text).digest('hex')
);
const processedData = await page.evaluate(async () => {
const sensitiveData = document.querySelector('#secure-data').value;
return await window.md5(sensitiveData);
});
Для сохранения ссылок на DOM-элементы между шагами Нодуль использует page.evaluateHandle:
const elementHandle = await page.evaluateHandle(() => {
return document.querySelector('.dynamic-content');
});
await page.evaluate(element => {
element.scrollIntoView();
}, elementHandle);
Эти техники гарантируют, что Нодуль может эффективно обрабатывать динамический контент, сохраняя при этом надежную производительность. Для пользователей с Prime-платформой поддерживается до 1,5 миллионов запусков сценариев в месяц, что обеспечивает широкие возможности автоматизации.
При работе с page.evaluate в автоматизации браузера могут возникать различные проблемы. Вот практические решения для их устранения и обеспечения более плавного выполнения.
Правильно настройте параметры TypeScript, чтобы избежать проблем, вызванных транспиляцией. Например:
// Используйте прямые, нетранспилированные функции
await page.evaluate(() => {
document.querySelector('#button').click();
});
await page.evaluate(`(async () => {
document.querySelector('#button').click();
})()`);
Избегайте прямого возврата DOM-элементов из page.evaluate. Вместо этого используйте ElementHandle для лучшего управления:
// Неправильно: возврат DOM-элемента
const element = await page.evaluate(() => {
return document.querySelector('.dynamic-element');
});
// Правильно: использование ElementHandle
const element = await page.evaluateHandle(() => {
return document.querySelector('.dynamic-element');
});
Скрипты могут выполняться до полной загрузки страницы, что приводит к ошибкам тайминга. Используйте следующие стратегии:
// Ожидание навигации после действия
await Promise.all([
page.waitForNavigation(),
page.click('#submit-button')
]);
// Ожидание определенного условия
await page.waitForFunction(() => {
const element = document.querySelector('.lazy-loaded');
return element && element.dataset.loaded === 'true';
}, { timeout: 5000 });
Для динамических веб-сайтов используйте более точные механизмы ожидания:
// Ожидание определенных сетевых запросов
await page.waitForResponse(
response => response.url().includes('/api/data')
);
// Проверка наличия и видимости элементов
await page.waitForSelector('.dynamic-content', {
visible: true,
timeout: 3000
});
Чтобы избежать утечек памяти, тщательно управляйте ссылками на DOM. Вот как:
// Использование и удаление ElementHandle
const handle = await page.evaluateHandle(() => {
return document.querySelector('.temporary-element');
});
await handle.evaluate(element => {
// Выполнение операций
});
await handle.dispose(); // Удаление после использования
При работе с несколькими элементами безопасно передавайте данные между контекстами:
// Извлечение данных из DOM
const selector = '.product-price';
const price = await page.evaluate((sel) => {
const element = document.querySelector(sel);
return element ? element.textContent.trim() : null;
}, selector);
Для обработчиков событий обеспечьте их очистку, чтобы избежать "зависших" обработчиков:
await page.evaluate(() => {
const handler = () => console.log('clicked');
const button = document.querySelector('#button');
button.addEventListener('click', handler);
// Хранение ссылок для очистки
window._cleanupHandlers = window._cleanupHandlers || [];
window._cleanupHandlers.push(() => {
button.removeEventListener('click', handler);
});
});
Для достижения наилучших результатов с page.evaluate
сосредоточьтесь на улучшении производительности, сокращении ненужных переключений контекста и обеспечении безопасности. Вот как можно оптимизировать рабочие процессы автоматизации браузера.
Эффективное выполнение кода в контексте страницы экономит время и системные ресурсы. Вот несколько техник для ускорения скриптов:
// Блокировка ненужных ресурсов, таких как изображения и стили
await page.setRequestInterception(true);
page.on('request', request => {
if (['image', 'stylesheet'].includes(request.resourceType())) {
request.abort();
} else {
request.continue();
}
});
// Пакетные операции для снижения нагрузки
await page.evaluate(() => {
const results = [];
document.querySelectorAll('.product-item').forEach(item => {
results.push({
title: item.querySelector('.title').textContent,
price: item.querySelector('.price').textContent,
stock: item.querySelector('.stock').dataset.value
});
});
return results;
});
Выбор правильных селекторов также играет важную роль в производительности:
Частые переключения между контекстами Node.js и браузера могут замедлять работу. Вот как их минимизировать:
// Пример неэффективного переключения контекста
for (const item of items) {
await page.evaluate((i) => {
document.querySelector(`#item-${i}`).click();
}, item);
}
// Лучше: пакетные операции в одном переключении контекста
await page.evaluate((itemsList) => {
itemsList.forEach(i => {
document.querySelector(`#item-${i}`).click();
});
}, items);
Если нужно обработать данные в Node.js и передать их обратно в браузер, используйте открытые функции вместо частых переключений контекста:
await page.exposeFunction('processData', async (data) => {
// Обработка данных в Node.js
return transformedData;
});
await page.evaluate(async () => {
const result = await window.processData(documentData);
// Использование обработанных данных в браузере
});
После оптимизации производительности и переключения контекста сосредоточьтесь на безопасности скриптов. Вот несколько лучших практик:
// Всегда очищайте входные данные перед использованием
const sanitizedInput = sanitizeHtml(userInput);
await page.evaluate((input) => {
document.querySelector('#search').value = input;
}, sanitizedInput);
// Используйте обработку ошибок для критических операций
try {
await page.evaluate(() => {
if (!window.__securityCheck) {
throw new Error('Проверка безопасности не пройдена');
}
// Продолжение операции
});
} catch (error) {
console.error('Нарушение безопасности:', error);
}
Для рабочих процессов в Нодуле учитывайте следующие дополнительные советы:
Метод page.evaluate соединяет контексты Node.js и браузера, отправляя строкифицированную JavaScript-функцию для выполнения в браузере. Эта функция работает независимо от среды Node.js, поэтому необходимо тщательно управлять передачей данных.
Вот типичный пример для извлечения данных:
const data = await page.evaluate(async () => {
const results = document.querySelectorAll('.data-item');
return Array.from(results, item => ({
id: item.dataset.id,
value: item.textContent.trim()
}));
});
Что нужно помнить:
Эти основы закладывают фундамент для эффективного использования Puppeteer. Дополнительные инструменты могут ещё больше упростить задачи автоматизации.
Puppeteer предлагает несколько инструментов для расширения возможностей page.evaluate:
Например, открытие функций Node.js для браузера может упростить сложную обработку данных в рабочих процессах, таких как в Нодуле. В то время как page.evaluate хорошо работает с примитивами и JSON-сериализуемыми объектами, page.evaluateHandle необходим для работы со сложными объектами браузера, которые нельзя сериализовать.