Captures fiables avec un navigateur headless
Méthode pratique pour des captures headless fiables avec viewport du profil, état prêt, capture pleine page native, vérification du bas et mémoire des conteneurs.
Vous préférez la doc produit maintenue ?
Cet article a une page équivalente dans le centre de documentation. Utilisez les docs pour le flux canonique, les flags à jour et la référence durable.
Commencer par le viewport du profil
Une capture n'est utile que si elle représente la page que le navigateur devait rendre. Dans une session liée à un profil, le viewport est le premier élément du contrat de rendu. Il contrôle les retours à la ligne, les changements responsive, la navigation fixe, la taille des images et la quantité de contenu visible avant le défilement. Ce n'est pas une valeur provisoire que le script peut modifier pour faciliter la capture.
Une erreur fréquente consiste à laisser la bibliothèque d'automatisation choisir un viewport pratique. Le contexte peut être plus étroit que le profil choisi et la page se réorganise avant la création de l'image. Une autre erreur consiste à donner au viewport la hauteur du document pour obtenir une image pleine page. Cela change la mise en page responsive et le résultat ne représente plus la session du navigateur.
Lorsque le profil doit fournir les dimensions, ne configurez pas le viewport du
contexte. Avec Puppeteer, utilisez defaultViewport: null. Avec Playwright, créez
le contexte sans remplacer viewport. Si le produit exige une autre dimension,
inscrivez ce choix dans le profil et dans la spécification de capture, puis
conservez-le pour les exécutions comparables.
const browser = await chromium.launch({
executablePath: process.env.BROWSER_BINARY,
args: [`--bot-profile=${process.env.BROWSER_PROFILE}`],
});
const context = await browser.newContext();
const page = await context.newPage();
L'exemple ne contient volontairement aucun réglage de viewport. Cette absence est la partie importante. N'utilisez pas une fonction de resize pour faire coïncider le viewport avec la hauteur du document. Un document long doit rester long dans le viewport du profil.

Définir l'état prêt avec des signaux de la page
La fin de la navigation ne signifie pas que l'image est prête. La page peut avoir terminé sa navigation initiale alors que l'application place encore des cartes, charge un graphique, choisit la langue ou remplace une police de secours. Une capture prise entre ces étapes est une image valide d'un état que le lecteur ne devait pas voir.
Définissez un petit contrat de disponibilité pour chaque route de capture. Un marqueur créé par la page est souvent plus fiable qu'une attente fixe, car la page peut le créer lorsque le contenu requis est présent. Il peut s'agir d'un élément avec un attribut stable, d'un état visible ou d'un composant de route qui apparaît après la dernière modification de mise en page. Le contrat doit décrire le contenu visible et ne pas dépendre d'un événement privé du navigateur.
Un contrat utile couvre généralement les points suivants :
- Le conteneur principal existe et est visible.
- Les données de la vue demandée sont rendues.
- Les changements de mise en page qui suivent l'arrivée des données sont terminés.
- Les polices des titres, libellés et textes sont prêtes.
- Les images de la zone demandée sont chargées ou dans un état de remplacement voulu.
- Une capture pleine page possède un marqueur de fin clair.
Le marqueur de fin est important pour les pages qui assemblent leur contenu par étapes. La présence de la première carte ne dit rien sur la moitié basse d'un rapport. Attendre le marqueur de fin de la page donne une limite stable. Si la route n'a pas de fin naturelle, définissez une limite de capture explicite au lieu de considérer un document qui grandit sans fin comme terminé.
Un ordre d'attente concret
Le modèle suivant combine un marqueur de route, l'état des polices et celui des images. Il ne dort pas pendant un intervalle arbitraire. Adaptez les sélecteurs à chaque page et faites des marqueurs une partie du contrat de la route, plutôt qu'un sélecteur générique partagé partout.
async function waitForCaptureReady(page) {
await page.waitForLoadState('domcontentloaded');
await page.locator('[data-capture-ready]').waitFor({ state: 'visible' });
await page.locator('[data-capture-end]').waitFor({ state: 'visible' });
await page.evaluate(async () => {
await document.fonts.ready;
await Promise.all(
[...document.images].map(image =>
image.complete
? undefined
: new Promise(resolve => {
image.addEventListener('load', resolve, { once: true });
image.addEventListener('error', resolve, { once: true });
})
)
);
});
}
La branche des images traite une vignette optionnelle indisponible comme un remplacement terminé, afin qu'une ressource secondaire ne bloque pas le travail. Si chaque image est obligatoire, faites apparaître le marqueur seulement après les ressources requises et enregistrez clairement l'absence d'une ressource.
L'absence d'activité réseau peut servir de signal complémentaire, mais ne doit pas être le seul. Les mises à jour en direct, les connexions persistantes et les requêtes périodiques peuvent garder une page occupée alors que son contenu visible est prêt. À l'inverse, un rendu côté client peut finir sans nouvelle requête. Le contrat de la page doit exprimer l'état que le lecteur doit voir.
Évitez une pause fixe après la navigation. Elle fait attendre une page rapide et peut encore capturer trop tôt une page lente. Elle masque aussi la raison pour laquelle la page n'était pas prête. Un marqueur stable fournit un point vérifiable lorsque la route évolue.
Capturer le document sans étirer le viewport
Pour une image du viewport, utilisez l'option de capture normale. Pour une image du document, utilisez l'option native pleine page de la bibliothèque d'automatisation. Elle conserve le viewport du profil et inclut le contenu situé sous la ligne de flottaison.
await waitForCaptureReady(page);
await page.screenshot({
path: outputPath,
fullPage: true,
type: 'png',
});
Ne donnez pas au viewport la hauteur du document avant cet appel. Ce raccourci modifie la mise en page responsive, peut empêcher le défilement qui déclenche le chargement différé et produit une image qui ne représente plus le profil choisi. Il peut aussi créer une surface plus grande que les capacités du chemin graphique ou de la mémoire du conteneur.
Si la page contient du contenu différé, préparez-le avec un défilement normal avant la capture pleine page native. Le défilement demande à la page de charger la zone voulue. Il ne remplace pas la capture pleine page. Revenez à la position supérieure, confirmez le contrat de disponibilité, puis laissez l'option native recueillir le document.
Le défilement infini exige une limite explicite. Décidez si l'image s'arrête à l'ensemble déjà chargé, à une section nommée ou à un état final de la route. Ne laissez pas le travail défiler indéfiniment pendant que la page ajoute des éléments. Un rapport borné est plus facile à relire, à stocker et à comparer, et sa hauteur ne dépend pas du moment précis de la capture.
Éléments fixes et mise en page responsive
Les en-têtes collants, panneaux de consentement, boutons de discussion et barres flottantes peuvent couvrir le contenu d'une image pleine page. S'ils ne font pas partie du rapport, la route de capture doit proposer une présentation d'impression ou d'archive. Une petite feuille de style réservée à la capture est souvent plus claire que la suppression d'éléments après la création de l'image. Gardez le viewport du profil inchangé.
Utilisez le même viewport pour les exécutions comparables. Un changement de largeur modifie les retours à la ligne et la position des cartes même si les données sont identiques. Un changement du facteur de pixels de l'appareil modifie aussi les dimensions de sortie et la mémoire nécessaire pendant la création. Enregistrez ces valeurs dans les métadonnées de la capture, plutôt que de les laisser au hasard.
Vérifier la partie basse des images longues
La partie basse d'une image longue mérite une vérification séparée. Le haut peut sembler correct alors que le pied, la dernière ligne, les bordures ou le fond sont coupés. Un espace réservé au chargement peut aussi rester visible en bas. Ouvrir chaque image depuis son coin supérieur fait manquer ces problèmes.
Ajoutez à la page un marqueur de fin stable et incluez-le dans l'exemple de revue. Il peut s'agir du titre du pied de page, de la dernière section d'un rapport ou d'une zone de fin compréhensible pour le lecteur. Il doit être visible dans l'image finale, et non être un marqueur caché de l'exécution du navigateur.
Vérifiez le bord inférieur de trois façons :
- Ouvrez un aperçu réduit et confirmez que le document atteint la fin attendue.
- Ouvrez la dernière partie à une échelle lisible et vérifiez la dernière ligne, le pied, les bordures et le remplissage du fond.
- Comparez les dimensions et la taille du fichier avec le relevé de capture. Un changement soudain peut signaler un déplacement de mise en page, une section absente ou une ressource qui n'est pas arrivée.
Les contrôles automatiques doivent porter sur le contrat de la page et sur le fichier produit. Confirmez que le marqueur de fin était visible avant la capture, que le fichier existe et que l'image peut être décodée. Gardez quelques pages longues représentatives pour une revue visuelle. Une comparaison pixel par pixel peut aider dans une suite maîtrisée, mais accompagnez-la d'une revue lisible : les polices, le rendu de la plateforme et des changements de contenu légitimes peuvent modifier des pixels isolés.
N'utilisez pas la hauteur brute du document comme seule condition de succès. La hauteur peut être connue avant que le contenu soit peint et continuer à augmenter après la création de l'image. Le marqueur de fin et la revue du fichier répondent à la question utile : le contenu attendu est-il arrivé dans l'image finale ?
Prévoir la mémoire partagée pour les images hautes
Une capture longue consomme plus que la taille du PNG ou du JPEG sur le disque. Le navigateur a besoin d'espace de travail pour composer la page, la couche d'automatisation en a besoin pour recevoir le résultat et l'encodeur en utilise pendant l'écriture. Un profil à haute densité de pixels agrandit la surface dans les deux directions. Un montage de mémoire partagée trop petit peut échouer uniquement sur la page la plus longue et ressembler à un incident intermittent.
Réservez la mémoire partagée en fonction du travail réel. Tenez compte du viewport
du profil, du facteur de pixels, du document le plus long approuvé, du format d'image
et des captures qui peuvent coexister dans le conteneur. Laissez une marge pour le
démarrage du navigateur et le rendu courant. Configurez explicitement la taille de
/dev/shm avec le runtime du conteneur ou le réglage équivalent de Compose, au lieu
d'accepter la petite valeur par défaut.
docker run --rm \
--shm-size="${BROWSER_SHM_SIZE}" \
-e BROWSER_PROFILE=/run/profiles/capture.enc \
screenshot-worker
La valeur appartient à la configuration de déploiement, car elle dépend de l'ensemble de pages approuvé. Ne recopiez pas la configuration d'une page courte pour un travail de documents longs. Lorsque l'ensemble évolue, recapturez la page longue représentative et examinez le résultat ainsi que les ressources du conteneur.
D'autres ressources comptent également. Le dossier de sortie doit accepter l'image complète et son écriture temporaire, le volume doit avoir les droits du worker, et le stockage du profil doit être séparé des artefacts finaux. Une écriture échouée ne doit pas remplacer l'image approuvée précédente par un fichier partiel. Écrivez dans un nom temporaire sur le même volume, fermez le fichier, puis utilisez l'opération atomique habituelle du système de fichiers.
Si la configuration Linux approuvée utilise Xvfb, sa surface doit couvrir le viewport du profil et utiliser le réglage de couleur validé pour le déploiement. Le mode headless natif peut ne pas avoir besoin de Xvfb. N'ajoutez pas un écran virtuel parce que l'image est haute. Choisissez un chemin pris en charge, validez-le avec les pages cibles et gardez-le identique en développement et en production.
Choisir le format selon l'usage
Le PNG est un choix prudent pour les textes, diagrammes, interfaces et régressions visuelles. Il conserve les contours nets et n'ajoute pas d'artefacts autour des petits libellés. Le JPEG peut convenir aux aperçus riches en photos lorsque la taille du fichier compte davantage que la précision des contours. Inscrivez le format dans la spécification de capture pour qu'un autre travail ne change pas discrètement le critère de revue.
Une sortie à haute densité de pixels est utile lorsque le lecteur doit agrandir un rapport ou lorsque l'image sert aux archives. Elle augmente aussi la mémoire, le transfert et le stockage. Le facteur de pixels doit venir du profil choisi. Ne le remplacez pas dans le contexte pour obtenir une image plus nette. Si le lecteur a besoin d'un aperçu plus petit, créez ce dérivé après avoir conservé la capture approuvée.
Utilisez des noms stables qui indiquent la route, la famille du profil, le mode, le format et la date, sans placer de données privées de la page dans le nom. Conservez l'artefact original lors de la création d'un dérivé. Une revue ultérieure pourra ainsi distinguer une modification de rendu d'une modification de miniature.
Un modèle de production court
La fonction de capture doit rendre l'ordre évident : créer une page liée au profil, naviguer, attendre le contrat de la page, utiliser la pleine page native si elle est demandée et fermer la page en cas d'échec.
async function capture(page, url, outputPath, fullPage = false) {
await page.goto(url, { waitUntil: 'domcontentloaded' });
await waitForCaptureReady(page);
await page.screenshot({
path: outputPath,
fullPage,
type: 'png',
});
}
Si plusieurs routes utilisent la même politique de session approuvée, créez le navigateur en dehors de cette fonction. Gardez la propriété des pages explicite, fermez une page après la fin de son artefact et transmettez l'erreur avec la route et le mode de capture. Une capture échouée doit être visible pour l'exécuteur, pas remplacée silencieusement par un ancien fichier.
Dans un lot, chaque page doit passer par les mêmes vérifications de disponibilité et d'artefact. N'ajoutez pas une voie rapide qui ignore le marqueur de fin pour les pages suivantes. La première page paraît souvent saine, tandis qu'une page lente révèle un manque de mémoire, une police tardive ou un document en croissance. Le même contrat facilite l'interprétation de ces différences.
Symptômes et ordre de vérification
L'image est vide ou presque vide. Vérifiez que la navigation est arrivée sur la route attendue, que le marqueur prêt est apparu et que le chemin d'affichage choisi était disponible. Examinez la sortie du navigateur pour savoir si la page s'est arrêtée avant le marqueur, plutôt que d'ajouter une pause.
L'image s'arrête avant la dernière section. Vérifiez le marqueur de fin, préparez le contenu différé avec un défilement normal et confirmez que le défilement infini a une limite. Examinez ensuite la partie basse de l'artefact.
La position du texte change entre deux captures. Vérifiez le même viewport du profil, facteur de pixels, polices, thème de couleur et données de page. Attendez la fin des polices et examinez les mouvements de mise en page avant la capture.
Le conteneur échoue seulement sur les pages longues. Vérifiez ensemble le montage de mémoire partagée, l'espace temporaire, le volume de sortie et la surface d'affichage. Une page courte ne prouve pas que la plus longue image tient. Ajustez la ressource, recapturez la page représentative et conservez le réglage avec le relevé du travail.
Un contrôle flottant couvre le rapport. Utilisez la présentation d'archive de la route ou capturez un élément de contenu utile. Ne modifiez pas les pixels après coup et confirmez que la présentation garde le viewport du profil.
Un espace réservé au chargement reste dans l'image. Faites attendre au marqueur prêt la ressource nécessaire, ou déclarez le remplacement volontaire. L'état réseau général ne dit pas si un espace réservé convient au lecteur.
Vérifier avant de créer une référence
Conservez un petit ensemble représentatif de routes pour la revue du déploiement. Ajoutez une page courte, un rapport long, une page avec des polices web, une page avec des médias différés et une mise en page responsive utilisant le profil choisi. Le but n'est pas de réunir toutes les routes, mais de couvrir les états qui peuvent changer la limite de l'image ou la demande de ressources.
Pour chaque route, notez le profil, la source du viewport, le mode, le format, le chemin d'affichage, la politique de mémoire du conteneur, le marqueur prêt et le marqueur de fin. Revoyez le premier écran et la partie basse. Confirmez que l'artefact peut être décodé, qu'il a l'orientation attendue et qu'il est écrit au bon endroit. Quand un changement est voulu, mettez aussi à jour le relevé de capture.
Une capture fiable est un flux de rendu, pas un dernier clic. Le profil fournit un viewport stable, la page fournit un état prêt compréhensible, l'option pleine page native atteint le contenu inférieur sans modifier le viewport et la revue vérifie la partie la plus facile à perdre. La mémoire partagée et le stockage complètent le contrat pour les documents longs et à haute densité de pixels. Les images restent ainsi utiles pour la revue visuelle, l'archivage et les contrôles de qualité autorisés dans différents environnements.
Articles Connexes
Faites passer BotBrowser de la recherche à la production
Utilisez ces guides pour comprendre le modèle, puis passez à la validation multi-plateforme, aux contextes isolés et au déploiement navigateur prêt pour l'échelle.