Latenode

Exécuter du JavaScript dans le contexte de la page avec page.evaluate dans Puppeteer

Découvrez comment utiliser efficacement la méthode page.evaluate de Puppeteer pour exécuter du JavaScript dans le contexte du navigateur lors de tâches d’automatisation web.

11 min de lecture
Schéma illustrant l’exécution de JavaScript dans Puppeteer via page.evaluate

page.evaluate() est une méthode clé de Puppeteer qui vous permet d’exécuter JavaScript directement dans le contexte du navigateur. Elle fait le lien entre Node.js et le navigateur, ce qui permet des tâches telles que la manipulation du DOM, l’extraction de données et l’autatisation de pages web dynamiques. Voici ce que vous devez savoir :

  • Ce qu’elle fait : exécute JavaScript dans le navigateur, comme si vous utilisiez la console du navigateur.
  • Comment elle fonctionne : convertit une fonction en chaîne de caractères, l’envoie au navigateur, l’exécute et renvoie le résultat.
  • Principales utilisations :
    • Extraction de données depuis des sites web (par exemple, texte, tableaux, JSON).
    • Automatisation des soumissions de formulaires et des interactions utilisateur.
    • Gestion de contenus dynamiques tels que le défilement infini ou les mises à jour AJAX.
  • Limites : les fonctions doivent être sérialisables en JSON, et les variables Node.js ne sont pas directement accessibles dans le contexte du navigateur.

Exemple rapide :

const title = await page.evaluate(() => document.title);

Cela récupère directement le titre de la page depuis le navigateur.

Comparaison : contexte Node.js vs. navigateur

FonctionnalitéContexte Node.jsContexte du navigateur
Objets globauxprocess, requirewindow, document
Emplacement du scriptMachine localePage web cible
Accès aux APIAPI Node.jsAPI Web du navigateur

Utilisez page.evaluate() pour des tâches d’automatisation précises et efficaces, en particulier avec des sites web fortement basés sur JavaScript.

NodeJS : Nodejs/Puppeteer - Comment utiliser page.evaluate

Explication du contexte de page

Lorsque vous utilisez Puppeteer pour l’automatisation web, il est essentiel de comprendre la différence entre le contexte Node.js et le contexte du navigateur. Ces deux environnements sont isolés et possèdent chacun leurs propres règles d’exécution du code et d’échange de données.

Comparaison entre les contextes Node.js et navigateur

Puppeteer fonctionne dans deux environnements : le contexte Node.js, où votre script principal s’exécute, et le contexte du navigateur, où les interactions avec la page web ont lieu. Il s’agit de processus distincts, chacun disposant de sa propre machine virtuelle [3].

Voici une comparaison rapide de leurs principales caractéristiques :

FonctionnalitéContexte Node.jsContexte du navigateur
Objets globauxprocess, require, __dirnamewindow, document, localStorage
Emplacement du scriptMachine localePage web cible
Portée des variablesPortée du script PuppeteerPortée du contexte de page
Accès aux APIAPI Node.jsAPI Web du navigateur
Espace mémoireProcessus distinctProcessus du navigateur

Comment fonctionne la communication entre contextes

L’échange de données entre ces contextes suit plusieurs étapes et repose largement sur la sérialisation :

  1. La fonction est convertie en chaîne de caractères avec Function.prototype.toString() [1].
  2. Cette chaîne est envoyée au navigateur via le Chrome DevTools Protocol [1].
  3. Le navigateur évalue la fonction dans son environnement.
  4. Les résultats sont sérialisés en JSON puis renvoyés au contexte Node.js [1].

Limites principales : les fonctions exécutées dans le contexte du navigateur ne peuvent pas accéder directement aux variables de la portée Node.js. Puppeteer propose des outils spécifiques pour répondre à ces défis :

  • page.evaluateHandle() : renvoie des références vers des objets du contexte du navigateur [1].
  • page.exposeFunction() : permet au navigateur d’appeler des fonctions Node.js [1].
  • evaluateOnNewDocument() : exécute du code avant le chargement des scripts de la page [1].

Toutefois, la sérialisation JSON peut supprimer certaines propriétés, notamment pour des objets complexes comme les nœuds DOM [2]. Pour éviter les problèmes, transmettez les données sous forme d’arguments de fonction plutôt que de vous appuyer sur des variables Node.js [3].

La maîtrise de ces techniques de communication vous permet d’utiliser efficacement page.evaluate pour les tâches d’automatisation. Nous allons maintenant examiner des exemples pratiques pour voir ces concepts en action.

Premiers pas avec page.evaluate

Structure de la méthode et paramètres

Syntaxe :

await page.evaluate(pageFunction, ...args)
ParamètreTypeDescription
pageFunctionFonction ou chaîneCode JavaScript à exécuter dans le contexte du navigateur
argsParamètres facultatifsValeurs transmises de Node.js au contexte du navigateur
Valeur de retourPromiseSe résout avec la valeur renvoyée par la fonction

La pageFunction peut être une fonction ou une chaîne contenant du code JavaScript. L’utilisation d’une fonction est généralement préférable pour le débogage et la compatibilité avec TypeScript. Vous trouverez ci-dessous quelques exemples illustrant son fonctionnement.

Exemples de code de base

Exemples :

  • Extraire le texte du premier <h1> directement depuis le DOM :
const headingText = await page.evaluate(() => {
    return document.querySelector('h1').textContent;
});
  • Automatiser la soumission d’un formulaire en transmettant des paramètres :
await page.evaluate((username, password) => {
    document.getElementById('username').value = username;
    document.getElementById('password').value = password;
    document.querySelector('#login-form').submit();
}, 'myUsername', 'myPassword');
  • Manipuler le DOM en ajoutant un nouvel élément :
await page.evaluate(() => {
    const div = document.createElement('div');
    div.textContent = 'Added by Puppeteer';
    document.body.appendChild(div);
    return div.textContent;
});

Points clés pour le développement

  • Les fonctions s’exécutent de manière isolée de votre code Node.js.
  • Les arguments transmis à la fonction doivent être sérialisables en JSON.
  • Les valeurs renvoyées sont automatiquement encapsulées dans une Promise.
  • La gestion d’objets complexes tels que les nœuds DOM exige une attention particulière.

Conseil de débogage : utilisez la configuration suivante pour activer le débogage durant le développement :

const browser = await puppeteer.launch({
    headless: false,
    slowMo: 100 // Adds a 100ms delay to each operation
});

Nous allons maintenant explorer les techniques d’échange de données entre les contextes Node.js et navigateur.

Échange de données entre contextes

Paramètres d’entrée

Lorsque vous transférez des données avec page.evaluate, limitez-vous aux valeurs sérialisables en JSON pour les arguments d’entrée.

Voici un aperçu rapide des types de paramètres pris en charge :

Type de paramètrePris en charge ?Exemple
Primitives✓ Entièrement'text', 42, true
Tableaux/objets✓ Compatibles JSON{ key: 'value' }, [1, 2, 3]
Fonctions✗ Pas directementUtilisez page.exposeFunction
Éléments DOM✓ Via JSHandleUtilisez page.evaluateHandle

Voyons maintenant comment ces valeurs sont renvoyées depuis le contexte du navigateur.

Gestion des sorties

Lors de l’utilisation de page.evaluate, les valeurs renvoyées sont automatiquement sérialisées en JSON. Voici comment cela fonctionne :

// Returning a simple value
const pageTitle = await page.evaluate(() => document.title);

// Returning a complex object
const metrics = await page.evaluate(() => ({
    viewport: window.innerWidth,
    scrollHeight: document.body.scrollHeight,
    timestamp: Date.now()
}));

« En règle générale, si la valeur de retour de la fonction donnée est plus complexe qu’un objet JSON (par exemple, la plupart des classes), alors evaluate renverra probablement une valeur tronquée (ou {}). En effet, nous ne renvoyons pas la valeur de retour réelle, mais une version désérialisée résultant du transfert de cette valeur via un protocole vers Puppeteer. » [1]

Après avoir récupéré la sortie, vous pouvez rencontrer des difficultés liées à la sérialisation. Voici comment les résoudre.

Gestion des problèmes de sérialisation

Certains cas courants nécessitent des solutions spécifiques :

  • Utiliser des éléments DOM
const bodyHandle = await page.$('body');
const html = await page.evaluate(body => body.innerHTML, bodyHandle);
await bodyHandle.dispose(); // Always clean up to avoid memory leaks
  • Utiliser des fonctions Node.js
await page.exposeFunction('md5', text =>
    crypto.createHash('md5').update(text).digest('hex')
);

const hash = await page.evaluate(async () => {
    return await window.md5('test-string');
});
  • Ajuster les paramètres du transpileur

Si vous utilisez TypeScript, assurez-vous que votre transpileur est correctement configuré :

// tsconfig.json
{
    "compilerOptions": {
        "target": "es2018"
    }
}

Ces stratégies vous aideront à gérer efficacement l’échange de données dans différents contextes.

sbb-itb-23997f1

Exemples pratiques

Voici comment utiliser page.evaluate dans des situations réelles, avec des extraits de code pratiques.

Extraction de données

Exemple : extraction des détails produit

Ce script collecte des informations telles que le titre, le prix, la note et l’état du stock depuis les fiches produit d’une page web :

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('Available')
  }));
});

Exemple : extraction des données d’un tableau

Cette approche récupère les données d’un tableau en parcourant ses lignes et ses colonnes :

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);
  });
});

Automatisation des formulaires

Automatisation de formulaire de base

Voici comment remplir les champs d’un formulaire, déclencher des événements et soumettre le formulaire :

await page.evaluate(() => {
  // Fill form fields
  document.querySelector('#username').value = 'testuser';
  document.querySelector('#password').value = 'secretpass';

  // Trigger input events for dynamic forms
  const event = new Event('input', { bubbles: true });
  document.querySelector('#username').dispatchEvent(event);

  // Submit form
  document.querySelector('form').submit();
});

Gestion des formulaires complexes

Pour des tâches telles que la sélection d’options dans une liste déroulante ou l’activation de boutons radio :

await page.evaluate(() => {
  // Select dropdown option
  const select = document.querySelector('#country');
  select.value = 'US';
  select.dispatchEvent(new Event('change', { bubbles: true }));

  // Check radio button
  const radio = document.querySelector('input[value="express"]');
  radio.checked = true;
  radio.dispatchEvent(new Event('change', { bubbles: true }));
});

Gestion des éléments dynamiques

Exemple : défilement infini

Ce script fait défiler une page jusqu’à collecter au moins 100 éléments :

const items = await page.evaluate(async () => {
  const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
  const items = new Set();

  while (items.size < 100) {
    // Scroll to bottom
    window.scrollTo(0, document.body.scrollHeight);

    // Wait for new content
    await delay(1000);

    // Collect items
    document.querySelectorAll('.item').forEach(item => 
      items.add(item.textContent.trim())
    );
  }

  return Array.from(items);
});

Exemple : gestion du contenu AJAX

Pour charger davantage de contenu dynamiquement, ce script clique sur un bouton « Charger plus » et attend l’apparition de nouveaux éléments :

await page.evaluate(async () => {
  // Click load more button
  document.querySelector('#loadMore').click();

  // Wait for content update
  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
    });
  });
});

Ces exemples montrent comment gérer divers cas d’usage tels que l’extraction de données, l’automatisation de formulaires et le contenu dynamique. Vous pouvez les adapter selon la structure et le comportement spécifiques de la page web avec laquelle vous travaillez.

Utiliser page.evaluate dans Latenode

Latenode intègre les fonctionnalités essentielles de Puppeteer dans ses workflows d’automatisation, ce qui simplifie l’exécution de JavaScript directement dans le navigateur. Avec page.evaluate, les utilisateurs peuvent manipuler le DOM et extraire des données efficacement. Cette approche permet d’intégrer facilement des opérations avancées de traitement des données et du DOM dans l’environnement d’automatisation de Latenode.

Scripts de navigateur dans Latenode

Le module d’automatisation de navigateur de Latenode utilise page.evaluate pour gérer aussi bien des tâches DOM simples que des exécutions JavaScript plus complexes. Voici son fonctionnement dans différents cas de figure :

// Basic DOM interaction
await page.evaluate(() => {
  const loginButton = document.querySelector('#login');
  loginButton.click();

  // Trigger a custom event
  loginButton.dispatchEvent(new Event('customClick'));
});

// Processing data with exposed functions
await page.exposeFunction('processData', async (data) => {
  // Process data in Node.js context
  return transformedData;
});

await page.evaluate(async () => {
  const rawData = document.querySelector('#data').textContent;
  const processed = await window.processData(rawData);
  return processed;
});

Latenode conserve également un journal de l’historique d’exécution, ce qui facilite le débogage des scripts.

Exemples d’automatisation

Latenode est parfaitement équipé pour gérer le contenu dynamique et les tâches d’automatisation complexes. Voici un exemple de traitement de contenu dynamique sur une page :

const extractProductData = await page.evaluate(async () => {
  const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

  // Wait for dynamic content to load
  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
    }));
});

Pour des opérations plus avancées, page.exposeFunction permet une interaction fluide entre Node.js et le navigateur :

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);
});

Pour conserver des références aux éléments DOM entre les étapes, Latenode utilise page.evaluateHandle :

const elementHandle = await page.evaluateHandle(() => {
  return document.querySelector('.dynamic-content');
});

await page.evaluate(element => {
  element.scrollIntoView();
}, elementHandle);

Ces techniques permettent à Latenode de gérer efficacement le contenu dynamique tout en assurant des performances fiables. Pour les utilisateurs de l’offre Prime, la plateforme prend en charge jusqu’à 1,5 million d’exécutions de workflows chaque mois, offrant ainsi d’importantes capacités d’automatisation.

Guide de résolution des erreurs

Lorsque vous utilisez page.evaluate dans l’automatisation de navigateur, vous pouvez rencontrer différents problèmes. Voici des solutions pratiques pour les résoudre et assurer une exécution plus fluide.

Corriger les erreurs de contexte

Configurez correctement vos paramètres TypeScript afin d’éviter les problèmes causés par la transpilation. Par exemple :

// Use direct, non-transpiled functions
await page.evaluate(() => {
  document.querySelector('#button').click();
});

await page.evaluate(`(async () => {
  document.querySelector('#button').click();
})()`);

Évitez de renvoyer directement des éléments DOM depuis page.evaluate. Utilisez plutôt ElementHandle pour une meilleure gestion :

// Incorrect: Returning a DOM element
const element = await page.evaluate(() => {
  return document.querySelector('.dynamic-element');
});

// Correct: Using ElementHandle
const element = await page.evaluateHandle(() => {
  return document.querySelector('.dynamic-element');
});

Résoudre les problèmes de temporisation

Les scripts peuvent s’exécuter avant le chargement complet de la page, ce qui entraîne des erreurs de temporisation. Utilisez ces stratégies pour gérer ces situations :

// Wait for navigation after an action
await Promise.all([
  page.waitForNavigation(),
  page.click('#submit-button')
]);

// Wait for a specific condition
await page.waitForFunction(() => {
  const element = document.querySelector('.lazy-loaded');
  return element && element.dataset.loaded === 'true';
}, { timeout: 5000 });

Pour les sites web dynamiques, adoptez des mécanismes d’attente plus ciblés :

// Wait for specific network requests
await page.waitForResponse(
  response => response.url().includes('/api/data')
);

// Ensure elements are both present and visible
await page.waitForSelector('.dynamic-content', {
  visible: true,
  timeout: 3000
});

Gérer les références DOM

Pour éviter les fuites mémoire, gérez soigneusement les références DOM. Voici comment procéder :

// Use and dispose ElementHandles
const handle = await page.evaluateHandle(() => {
  return document.querySelector('.temporary-element');
});
await handle.evaluate(element => {
  // Perform operations
});
await handle.dispose(); // Dispose of handle after use

Lorsque vous travaillez avec plusieurs éléments, transmettez les données de façon sûre entre les contextes :

// Extract data from the DOM
const selector = '.product-price';
const price = await page.evaluate((sel) => {
  const element = document.querySelector(sel);
  return element ? element.textContent.trim() : null;
}, selector);

Pour les écouteurs d’événements, assurez un nettoyage approprié afin d’éviter les gestionnaires persistants :

await page.evaluate(() => {
  const handler = () => console.log('clicked');
  const button = document.querySelector('#button');
  button.addEventListener('click', handler);

  // Store cleanup references
  window._cleanupHandlers = window._cleanupHandlers || [];
  window._cleanupHandlers.push(() => {
    button.removeEventListener('click', handler);
  });
});

Bonnes pratiques de mise en œuvre

Pour obtenir les meilleurs résultats avec page.evaluate, vous devez vous concentrer sur l’amélioration des performances, la réduction des changements de contexte inutiles et la sécurité. Voici comment optimiser vos workflows d’automatisation de navigateur.

Optimisation des performances

L’exécution efficace du code dans le contexte de la page permet de gagner du temps et d’économiser des ressources système. Vous trouverez ci-dessous quelques techniques pour accélérer vos scripts :

// Block unnecessary resources like images and stylesheets
await page.setRequestInterception(true);
page.on('request', request => {
  if (['image', 'stylesheet'].includes(request.resourceType())) {
    request.abort();
  } else {
    request.continue();
  }
});

// Batch operations to reduce overhead
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;
});

Le choix des bons sélecteurs joue également un rôle important dans les performances :

Type de sélecteurVitesseExemple
IDLe plus rapide#main-content
ClasseRapide.product-item
BaliseModéréediv > span
XPath complexeLe plus lent//div[@class='wrapper']//span

Gestion des changements de contexte

Les changements de contexte entre Node.js et l’environnement du navigateur peuvent ralentir l’exécution. Voici comment les limiter :

// Example of inefficient context switching
for (const item of items) {
  await page.evaluate((i) => {
    document.querySelector(`#item-${i}`).click();
  }, item);
}

// Better: Batch operations in a single context switch
await page.evaluate((itemsList) => {
  itemsList.forEach(i => {
    document.querySelector(`#item-${i}`).click();
  });
}, items);

Si vous devez traiter des données dans Node.js et les renvoyer au navigateur, exposez des fonctions au lieu de basculer continuellement entre les contextes :

await page.exposeFunction('processData', async (data) => {
  // Process data in Node.js
  return transformedData;
});

await page.evaluate(async () => {
  const result = await window.processData(documentData);
  // Use the processed data in the browser
});

Consignes de sécurité

Une fois les performances et les changements de contexte optimisés, concentrez-vous sur la sécurité de vos scripts. Voici quelques bonnes pratiques :

// Always sanitize inputs before using them
const sanitizedInput = sanitizeHtml(userInput);
await page.evaluate((input) => {
  document.querySelector('#search').value = input;
}, sanitizedInput);

// Use error handling for critical operations
try {
  await page.evaluate(() => {
    if (!window.__securityCheck) {
      throw new Error('Security check failed');
    }
    // Continue with the operation
  });
} catch (error) {
  console.error('Security violation:', error);
}

Pour les workflows Latenode, tenez également compte des conseils suivants :

  • Utilisez userDataDir pour mettre les ressources en cache et améliorer les performances entre les sessions.
  • Fermez les pages et instances de navigateur inutilisées afin d’économiser de la mémoire.
  • Gérez les captures d’écran avec des buffers plutôt qu’en vous appuyant sur des opérations du système de fichiers.
  • Mettez en œuvre une gestion robuste des erreurs et des contrôles de sécurité approfondis.

Résumé

Récapitulatif des points clés

La méthode page.evaluate relie les contextes Node.js et navigateur en envoyant au navigateur une fonction JavaScript convertie en chaîne de caractères pour y être exécutée. Cette fonction fonctionne indépendamment de l’environnement Node.js ; vous devez donc gérer soigneusement le transfert des données.

Voici un exemple courant d’extraction de données :

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()
  }));
});

Points à garder à l’esprit :

  • Les arguments doivent être sérialisables en JSON.
  • Les valeurs renvoyées sont automatiquement désérialisées.
  • Les API du navigateur sont disponibles uniquement dans le contexte evaluate.
  • Les variables Node.js ne sont pas accessibles dans le contexte du navigateur.

Ces bases constituent le fondement d’une utilisation efficace de Puppeteer. Des outils supplémentaires peuvent encore simplifier vos tâches d’automatisation.

Outils Puppeteer supplémentaires

Puppeteer propose plusieurs outils pour étendre les capacités de page.evaluate :

OutilObjectifCas d’usage idéal
page.evaluateHandleRenvoie des références d’objetsInteragir directement avec des éléments DOM
page.exposeFunctionRend les fonctions Node.js utilisables dans le navigateurGérer une logique complexe côté serveur
page.evaluateOnNewDocumentExécute des scripts avant le chargement d’une pagePréparer l’environnement du navigateur à l’avance

Par exemple, exposer des fonctions Node.js au navigateur peut simplifier le traitement avancé des données dans des workflows tels que ceux de Latenode. Si page.evaluate fonctionne bien pour gérer les types primitifs et les objets sérialisables en JSON, page.evaluateHandle est indispensable pour travailler avec des objets complexes du navigateur qui ne peuvent pas être sérialisés.

References

FAQ

Frequently Asked Questions

page.evaluate() exécute du JavaScript dans le contexte du navigateur, sérialise la fonction vers la page via le protocole Chrome DevTools, l’exécute sur le DOM et renvoie des résultats sérialisables en JSON à votre script Node.js.

Cela vous a aidé ? Partagez-le →

Écrit par

Vasiliy Datsenko

Responsable du support client

Vasiliy Datsenko est responsable du support client chez Latenode et un rédacteur en automatisation axé sur les produits. Son travail relie les conversations clients, la recherche sur l'automatisation des flux de travail, les cas d'utilisation de l'IA et la formation pratique sur les produits pour les équipes cherchant à automatiser des processus métier réels.

Profil de l'auteur →

Vérifié par

Oleg Zankov

PDG de Latenode, expert en no-code

Avec une philosophie ancrée dans l'innovation, la résolution de problèmes et l'expérience utilisateur, je me consacre à donner aux équipes les moyens de créer des intégrations sur mesure et d'automatiser les workflows avec facilité et efficacité. Fort d'une riche expérience en développement commercial, entrepreneurship technologique et développement logiciel, j'ai reconnu le besoin d'une solution d'intégration plus accessible, évolutive et adaptable. Ainsi est né Latenode.com. Grâce à notre plateforme, les entreprises peuvent exploiter la puissance de la technologie sans nécessiter de compétences approfondies en codage. Passionné par la création d'un avenir où la technologie nous sert, et non l'inverse, ma mission est de simplifier les processus complexes. Je crois en la démocratisation de la technologie et en dotant les équipes des outils nécessaires pour innover, croître et réussir dans un monde de plus en plus numérique.

Profil de l'auteur →

Continuer la lecture