Автоматизируйте. Меняйте.
Развивайте.
help@nodul.ru
info@nodul.ru
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 необходим для работы со сложными объектами браузера, которые нельзя сериализовать.