Puppeteer est une bibliothèque Node.js qui fournit une API puissante pour contrôler Chrome et Chromium sans tête via le protocole DevTools. L'une de ses fonctionnalités les plus utiles est la possibilité de capturer par programme des captures d'écran de pages et d'éléments Web.
Pour les scrapers Web, la possibilité de prendre des captures d'écran avec Puppeteer ouvre une variété de cas d'utilisation précieux :
- Débogage visuel des problèmes de scraping et des échecs de tests.
- Capturer les états des pages dynamiques et des SPA.
- Surveillance des régressions visuelles et des modifications de l'interface utilisateur.
- Création de tutoriels et de documentation avec des captures d'écran pour le contexte.
- Générer des ressources d'image à partir de pages Web.
Dans ce guide complet, nous explorerons comment exploiter les captures d'écran de Puppeteer pour améliorer vos flux de travail de scraping Web.
L'essor de Puppeteer pour le Web Scraping
Puppeteer a été publié pour la première fois en 2017 et a été rapidement adopté par la communauté du web scraping. Voici quelques statistiques qui mettent en évidence sa popularité :
- Plus de 52,000 XNUMX étoiles sur Github, ce qui en fait l'un des meilleurs projets JS.
- Plus de 3 millions de téléchargements hebdomadaires sur NPM.
- Croissance de 490 % d'une année sur l'autre des recherches Google pour Marionnettiste en 2022.
Alors, qu'est-ce qui distingue Puppeteer en matière de web scraping ?
Contrôle du navigateur sans tête
Puppeteer offre un contrôle total sur un navigateur sans tête via le protocole Chrome DevTools. Cela permet de répliquer les interactions des utilisateurs pour l'automatisation et la suppression du contenu dynamique.
Léger et rapide
Être uniquement sans tête signifie que Puppeteer ignore tout le rendu de l'interface utilisateur qui fait du Chromium un poids lourd. Cela se traduit par des performances rapides pour le scraping à grande échelle.
Développement actif
Soutenu par l'équipe Chrome de Google, Puppeteer bénéficie de mises à jour fréquentes et de nouvelles fonctionnalités adaptées aux cas d'utilisation d'automatisation et de scraping.
Plus simple que le sélénium
Puppeteer se concentre uniquement sur le contrôle de Chromium alors que Selenium prend en charge plusieurs navigateurs. L'API est beaucoup plus propre et idiomatique, ce qui la rend facile à utiliser.
Pour ces raisons, de nombreux web scrapers passent de Selenium/WebDriver à Puppeteer pour améliorer la vitesse, la fiabilité et les capacités.
Voyons maintenant comment tirer parti des puissantes capacités de capture d'écran de Puppeteer.
Capturer des captures d'écran pleine page
Le moyen le plus simple de prendre une capture d'écran d'une page entière consiste à utiliser le page.screenshot()
méthode:
// Launch browser
const browser = await puppeteer.launch();
// Open page
const page = await browser.newPage();
await page.goto(‘https://example.com‘);
// Screenshot
await page.screenshot({
path: ‘fullpage.png‘
});
Cela capture la fenêtre d'affichage actuellement visible. Pour capturer la hauteur complète de la page, définissez le fullPage
Option de true
:
await page.screenshot({
path: ‘longpage.png‘,
fullPage: true
});
Spécification des options d'image
Le screenshot()
La méthode accepte des options pour contrôler le type, la qualité et plus :
type
– png, jpeg ou webp. La valeur par défaut est png.quality
– Pour jpeg/webp, la qualité varie de 0 à 100. La valeur par défaut est 80.omitBackground
– Masque le fond blanc par défaut et autorise la transparence.encoding
– Peut sortir en base64 au lieu d’enregistrer un fichier.
Par exemple, pour enregistrer un jpeg de haute qualité :
await page.screenshot({
path: ‘page.jpeg‘,
type: ‘jpeg‘,
quality: 100
});
Conseil : Utilisez webp pour une meilleure compression avec une qualité équivalente. Cependant, webp peut avoir des problèmes de compatibilité.
Gérer les grandes captures d'écran
Les captures d’écran d’une page entière peuvent facilement dépasser plusieurs mégaoctets. Par défaut, Puppeteer met les captures d'écran en mémoire tampon avant de les enregistrer, ce qui peut dépasser les limites du processus.
Pour gérer les grandes captures d'écran, passez l'option encoding: ‘base64‘
pour obtenir la chaîne base64 au lieu d'un Buffer. Enregistrez ensuite en utilisant fs.writeFile() pour éviter de mettre l'image en mémoire tampon.
Voici un exemple :
const buffer = await page.screenshot({ encoding: ‘base64‘ });
fs.writeFile(‘screenshot.png‘, buffer, ‘base64‘, err => {
// handle error
});
Défilement de grandes pages pour des captures de page entière
Pour capturer toute la hauteur des pages plus longues que la fenêtre d'affichage, nous devrons d'abord faire défiler la page.
Voici une approche utilisant page.evaluate()
:
// Scroll to bottom
await page.evaluate(() => {
window.scrollTo(0, document.body.scrollHeight);
});
// Screenshot full scrollable area
await page.screenshot({ path: ‘longpage.png‘, fullPage: true });
Nous pouvons également faire défiler progressivement les captures d'écran, puis les assembler en une seule grande capture d'écran. Cela évite d'avoir à mettre en mémoire tampon l'intégralité de l'image.
Alternative : Enregistrer au format PDF
Une autre option pour capturer du contenu d’une page entière : générez un PDF !
// Generates PDF and saves to disk
await page.pdf({
path: ‘page.pdf‘,
printBackground: true
});
Avantages des PDF :
- Gère le contenu de plusieurs pages immédiatement.
- Le format vectoriel génère généralement des fichiers de plus petite taille.
- Le formatage de l'impression reste intact.
Inconvénients:
- Moins flexible pour le traitement programmatique.
- Options de style limitées par rapport aux images.
- Peut-être ne pas capturer le contenu rendu dynamiquement.
Définition de la taille de la fenêtre
Par défaut, Puppeteer utilise une fenêtre d'affichage de 800 px x 600 px. Pour obtenir des captures d'écran complètes précises sur différentes tailles d'ordinateur de bureau et de mobile, nous pouvons définir explicitement la fenêtre d'affichage :
// 1200px wide desktop
await page.setViewport({
width: 1200,
height: 800
});
// 400px wide mobile
await page.setViewport({
width: 400,
height: 1200
});
Ensuite, les captures d'écran correspondront à la taille de fenêtre spécifiée.
Capturer des éléments
En plus des captures d'écran pleine page, nous pouvons capturer des captures d'écran d'éléments spécifiques en utilisant element.screenshot()
.
// Get reference to element
const menu = await page.$(‘.main-menu‘);
// Screenshot just that element
await menu.screenshot({path: ‘menu.png‘});
L’élément défilera avant de capturer la capture d’écran. Cela permet de capturer des images d'éléments qui pourraient être hors écran sans avoir à les faire défiler.
Quelques cas d'utilisation des captures d'écran d'éléments :
- Capturer des captures d'écran de composants dynamiques tels que des tickers ou des animations.
- Débogage des problèmes de mise en page en prenant des photos d'éléments individuels.
- Obtenir des éléments d'image d'icônes et d'illustrations.
Captures d'écran des éléments hors écran
Un problème courant est que des éléments sont masqués ou déplacés lorsque vous essayez de capturer des captures d'écran lors d'interactions.
Nous pouvons tirer parti du défilement automatique des éléments element.screenshot()
pour capturer de manière fiable des éléments dans n'importe quel état, même hors écran :
// Click button which hides the element
await page.click(‘.toggle-menu‘);
// Menu is now hidden but we can still screenshot it
await menu.screenshot({path: ‘hidden-menu.png‘});
Cela permet une capture d'écran facile sans réinitialiser l'état de la page.
En attente du chargement du contenu dynamique
Lorsque nous travaillons avec des pages dynamiques, nous souhaitons attendre que le contenu soit rendu avant de prendre des captures d'écran pour capturer l'état souhaité.
Voici un exemple en attente de l'apparition d'un élément :
// Click button to trigger ajax call
await page.click(‘.load-content‘);
// Wait for new content to load
await page.waitForSelector(‘.loaded‘);
// Screenshot after loaded
await page.screenshot({path: ‘loaded.png‘});
page.waitForSelector()
attend que le sélecteur existe dans le DOM avant de continuer.
Voici quelques autres attentes utiles :
page.waitFor()
– Attendre qu'une condition donnée soit vraie.page.waitForFunction()
– Attendez la fin des mises à jour asynchrones du DOM.page.waitUntil()
– Attendez que la navigation ait lieu.
La clé consiste à choisir la bonne condition d'attente pour la mise à jour de la page que vous souhaitez capturer dans une capture d'écran.
En attente de modifications spécifiques du DOM
Pour synchroniser avec des modifications DOM plus discrètes, nous pouvons attendre la mise à jour des attributs au lieu des sélecteurs généraux :
// Wait for text content to change
await page.waitForFunction(() => {
return document.querySelector(‘.status‘).textContent === ‘Loaded‘;
});
// Element updated
await page.screenshot({/*...*/});
Cette approche fonctionne bien pour attendre le chargement des données clés plutôt que pour les modifications statiques du DOM.
Gérer les applications à page unique (SPA)
Attendre les modifications du DOM peut être délicat avec les SPA JavaScript complexes qui mettent à jour l'état sans rechargement.
Quelques conseils pour les gérer :
- Attendez que le réseau soit inactif après les interactions pour permettre aux XHR de se terminer.
- Attendez que des composants spécifiques tels que les superpositions disparaissent au lieu des sélecteurs généraux.
- Faites défiler jusqu'à la section nécessaire pour forcer le rendu avant de prendre une capture d'écran.
- Utilisez des attentes incrémentielles au lieu de délais d'attente fixes.
Aucune approche unique ne fonctionne parfaitement pour tous les SPA. Vous devrez expérimenter avec l'application en question.
Faire défiler les pages avant de prendre des captures d'écran pleine page
Pour les pages qui nécessitent un défilement, nous devrons faire défiler par programme avant de prendre une capture d'écran complète avec fullPage: true
.
Voici une approche fiable :
await page.evaluate(() => {
// Scroll to bottom
window.scrollTo(0, document.body.scrollHeight);
});
// Capture full scrolled screenshot
await page.screenshot({fullPage: true});
Cela fait défiler la page jusqu'à la position de défilement maximale avant de prendre la capture d'écran.
Une alternative consiste à utiliser window.scrollBy()
pour faire défiler progressivement une certaine quantité à la fois. Cela permet de prendre des captures d'écran en continu tout en faisant défiler toute la page.
Gestion des longues pages défilantes
Pour les pages extrêmement longues, le défilement sur toute la longueur en une seule fois peut toujours dépasser les limites de mémoire ou de temps.
Une bonne solution consiste à le diviser en sections, à le faire défiler petit à petit, à prendre des captures d'écran et à les assembler :
const screenshots = [];
while (hasMoreContent()) {
await page.evaluate(scrollDown);
screenshots.push(await page.screenshot());
}
// Stitch screenshots together into one tall image
Cela évite d'avoir à mettre en mémoire tampon la hauteur complète de la page.
Faire défiler horizontalement également
Pour les pages avec défilement horizontal, nous pouvons ajuster la séquence de défilement pour qu'elle défile également horizontalement :
await page.evaluate(() => {
window.scrollTo(
document.body.scrollWidth,
document.body.scrollHeight
);
});
await page.screenshot({fullPage: true});
Cela capture la largeur et la hauteur de la page entière !
Meilleures pratiques pour des captures d'écran fiables
Voici quelques conseils clés pour prendre des captures d’écran cohérentes et fiables avec Puppeteer :
Attendez que le réseau soit inactif - Utilisation page.waitForNetworkIdle()
après les interactions pour garantir que toutes les demandes asynchrones sont terminées avant de capturer l'état.
Utilisez des attentes appropriées – Choisissez des attentes conditionnelles qui se synchronisent avec l'état de la page souhaité plutôt que des délais d'attente généraux.
Définir la taille de la fenêtre - Définissez explicitement la fenêtre d'affichage pour capturer des captures d'écran précises de l'appareil.
Bouclier contre les animations/popups – Le survol d’éléments peut déclencher des changements – utilisez page.evaluate()
pour éviter les effets secondaires.
Prévoyez du temps pour le rendu – Attendez quelques centaines de millisecondes après avoir fait défiler les pages pour terminer le rendu avant les captures d'écran.
Stabiliser les tests floconneux – Définissez une boucle de nouvelle tentative avec des attentes autour des étapes de capture d’écran pour gérer les flocons.
Comparez avec le bien connu – Tirez parti des outils de tests de régression visuelle pour détecter les changements involontaires.
Conclusion
J'espère que ce guide a fourni un aperçu complet de la prise de captures d'écran de pages entières et d'éléments avec Puppeteer pour vos besoins de scraping Web.
Quelques sujets clés que nous avons abordés :
- Utilisation de page.screenshot() et element.screenshot() pour capturer des captures d'écran
- Options de contrôle du type, de la qualité et du format de l'image
- Faire défiler les pages et attendre du contenu dynamique
- Définition de la taille de la fenêtre d'affichage pour les pages réactives
- Meilleures pratiques pour des flux de travail de capture d'écran fiables
Les captures d'écran automatisées sont inestimables pour le débogage des scrapers, les tests visuels et la capture d'états dynamiques. Ajoutez-les à votre boîte à outils de web scraping avec Puppeteer !