Captures d'écran en navigateur headless : bonnes pratiques et conseils
Comment capturer des captures d'écran cohérentes et de haute qualité en mode headless, couvrant le viewport, le DPI, les formats, le timing et la capture pleine page.
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.
Introduction
Les captures d'écran dans les environnements de navigateur headless sont d'une complexité trompeuse. Une capture d'écran qui semble parfaite en développement peut produire des images vides, de mauvaises dimensions, des polices manquantes ou des couleurs incorrectes en production. Les causes profondes vont de la mauvaise configuration du viewport et du timing de capture prématuré aux problèmes de configuration du serveur d'affichage et à la disponibilité des polices.
BotBrowser ajoute une dimension supplémentaire à la capture d'écran : la cohérence des empreintes. Le profil définit la résolution d'écran, le ratio de pixels de l'appareil et les dimensions de la fenêtre. Si votre configuration de capture d'écran entre en conflit avec ces valeurs, vous obtenez soit une sortie incorrecte, soit des incohérences d'empreinte. Ce guide couvre le workflow complet de capture d'écran, de la configuration de l'affichage au timing de capture en passant par la sélection de format, garantissant à la fois la qualité visuelle et la cohérence des empreintes.
Pourquoi les bonnes pratiques de capture d'écran sont importantes
Les captures d'écran servent de nombreux objectifs dans l'automatisation de navigateur : tests de régression visuelle, archivage de contenu, surveillance de pages et capture de données depuis du contenu visuellement rendu. Chaque cas d'usage a des exigences de qualité différentes, mais tous partagent des modes de défaillance communs.
Le problème le plus fréquent est le timing. Une capture d'écran prise avant que la page ne se charge complètement produit une image vide ou partiellement rendue. Les pages riches en JavaScript peuvent prendre plusieurs secondes après l'événement de chargement initial avant que tout le contenu ne soit visible. Le chargement des polices ajoute un délai supplémentaire, et les polices personnalisées qui ne chargent pas produisent un texte de repli qui ne ressemble en rien au design prévu.
Les inadéquations de viewport et de résolution sont le deuxième problème le plus courant. Une capture d'écran prise à la mauvaise taille de viewport capture soit trop, soit trop peu de contenu. Le ratio de pixels de l'appareil affecte la résolution de sortie : un viewport de 1920x1080 à 2x DPI produit une image de 3840x2160 pixels, ce qui peut ne pas être ce que vous attendez.
Dans les environnements headless, la configuration de l'affichage Xvfb affecte directement le rendu. Un affichage configuré en profondeur de couleur 16 bits produit des bandes de couleur visibles. Une résolution d'affichage plus petite que le viewport coupe le contenu rendu.
Contexte technique
Comment fonctionnent les captures d'écran dans Chrome headless
Lorsque vous demandez une capture d'écran via Playwright, Puppeteer ou CDP, voici ce qui se passe :
- Le composeur du navigateur rend la page dans un buffer hors écran.
- Les données de pixels sont capturées depuis ce buffer.
- Les données sont encodées dans le format demandé (PNG ou JPEG).
- L'image encodée est retournée en chaîne base64 ou écrite sur disque.
Les dimensions du viewport déterminent la taille du buffer de composition. Le ratio de pixels de l'appareil détermine le facteur de mise à l'échelle. Un viewport de 1920x1080 à 1x DPI produit une image de 1920x1080. Le même viewport à 2x DPI produit une image de 3840x2160 avec un texte et des graphiques plus nets.
Profil BotBrowser et captures d'écran
Les profils BotBrowser définissent screen.width, screen.height, window.innerWidth, window.innerHeight et devicePixelRatio. Ces valeurs affectent ce que JavaScript rapporte lorsqu'une page interroge les propriétés d'affichage, mais elles influencent aussi la mise en page et le rendu du contenu.
Pour des empreintes cohérentes, le viewport de rendu réel devrait correspondre aux valeurs du profil. Définir defaultViewport: null dans Puppeteer dit au framework de ne pas substituer ces valeurs.
Xvfb et qualité de rendu
Sur les serveurs Linux headless, Xvfb fournit l'affichage X11 que Chrome utilise pour l'initialisation du rendu. La configuration Xvfb affecte directement la qualité de sortie :
- Résolution : devrait être au moins aussi grande que le plus grand viewport que vous utilisez.
1920x1080couvre la plupart des profils de bureau.2560x1440fournit de la marge pour les viewports plus grands. - Profondeur de couleur : doit être 24 bits (
x24). Les profondeurs inférieures causent des bandes de couleur et affectent la sortie d'empreinte Canvas. - Numéro d'affichage : n'importe quel numéro inutilisé fonctionne.
:10est conventionnel pour les configurations BotBrowser.
<svg viewBox="0 0 700 260" xmlns="http://www.w3.org/2000/svg" style={{maxWidth: '100%', height: 'auto'}}>
Configuration et utilisation
Attente fiable du chargement de page
Combinez plusieurs stratégies d'attente pour une fiabilité maximale :
async function waitForPageReady(page, url) {
// Naviguer avec networkidle0 pour la plupart du contenu
await page.goto(url, {
waitUntil: 'networkidle0',
timeout: 30000,
});
// Attendre un élément de contenu spécifique si connu
try {
await page.waitForSelector('.main-content', {
visible: true,
timeout: 5000,
});
} catch (e) {
// L'élément peut ne pas exister sur toutes les pages
}
// Attendre que les polices finissent de charger
await page.evaluate(() => document.fonts.ready);
// Optionnel : attendre les images chargées paresseusement
await page.evaluate(async () => {
const images = document.querySelectorAll('img[loading="lazy"]');
await Promise.all(
Array.from(images)
.filter(img => !img.complete)
.map(img => new Promise(resolve => {
img.onload = resolve;
img.onerror = resolve;
}))
);
});
}
Captures d'écran pleine page
Capturer la page entière, y compris le contenu sous le pli :
await page.screenshot({
path: 'fullpage.png',
fullPage: true,
type: 'png',
});
Note : les captures d'écran pleine page peuvent être très volumineuses pour les pages longues. Une page de 1920 pixels de large qui défile jusqu'à 10 000 pixels produit une image de 19 200 000 pixels à 1x DPI.
Captures d'écran spécifiques à un élément
Capturer un élément spécifique sans la page environnante :
const element = await page.$('.target-element');
if (element) {
await element.screenshot({
path: 'element.png',
type: 'png',
});
}
Captures d'écran haute résolution
Pour des captures d'écran de qualité retina, utilisez un profil avec un ratio de pixels de 2x, ou substituez-le :
// Playwright : définir le facteur d'échelle de l'appareil dans le contexte
const context = await browser.newContext({
deviceScaleFactor: 2,
});
Le PNG résultant sera deux fois les dimensions du viewport dans chaque direction.
Sélection de format
PNG est sans perte et produit une sortie pixel-parfaite. Utilisez-le pour :
- Les tests de régression visuelle où la comparaison pixel exacte compte
- L'archivage où la qualité ne peut pas être compromise
- Les images avec du texte, des lignes ou des bords nets
JPEG est avec perte mais beaucoup plus petit. Utilisez-le pour :
- La capture à haut volume où le stockage compte
- Les miniatures ou aperçus où la qualité pixel-parfaite n'est pas nécessaire
- Les photographies ou images complexes où les artefacts de compression sont moins visibles
// PNG pour la qualité
await page.screenshot({ path: 'output.png', type: 'png' });
// JPEG pour la taille (qualité 0-100)
await page.screenshot({ path: 'output.jpg', type: 'jpeg', quality: 85 });
Fonction de capture d'écran de production
const puppeteer = require('puppeteer-core');
async function captureScreenshot(url, outputPath, options = {}) {
const {
format = 'png',
quality = 85,
fullPage = false,
waitSelector = null,
timeout = 30000,
} = options;
const browser = await puppeteer.launch({
executablePath: '/opt/botbrowser/chrome',
args: [
'--bot-profile=/opt/profiles/profile.enc',
'--window-size=1920,1080',
],
headless: true,
defaultViewport: null,
});
try {
const page = await browser.newPage();
// Naviguer et attendre le contenu
await page.goto(url, {
waitUntil: 'networkidle0',
timeout: timeout,
});
// Attendre un élément spécifique si fourni
if (waitSelector) {
await page.waitForSelector(waitSelector, {
visible: true,
timeout: 10000,
});
}
// Attendre les polices
await page.evaluate(() => document.fonts.ready);
// Capturer
const screenshotOptions = {
path: outputPath,
type: format,
fullPage: fullPage,
};
if (format === 'jpeg') {
screenshotOptions.quality = quality;
}
await page.screenshot(screenshotOptions);
console.log(`Screenshot saved: ${outputPath}`);
} finally {
await browser.close();
}
}
// Exemples d'utilisation
captureScreenshot('https://example.com', '/output/example.png');
captureScreenshot('https://example.com', '/output/example-full.jpg', {
format: 'jpeg',
quality: 90,
fullPage: true,
waitSelector: '.main-content',
});
Capture d'écran par lots
Pour capturer efficacement de nombreuses pages :
async function batchCapture(urls, outputDir) {
const browser = await puppeteer.launch({
executablePath: '/opt/botbrowser/chrome',
args: [
'--bot-profile=/opt/profiles/profile.enc',
'--window-size=1920,1080',
],
headless: true,
defaultViewport: null,
});
try {
for (let i = 0; i < urls.length; i++) {
const page = await browser.newPage();
try {
await page.goto(urls[i], {
waitUntil: 'networkidle0',
timeout: 20000,
});
await page.evaluate(() => document.fonts.ready);
await page.screenshot({
path: `${outputDir}/page-${i}.png`,
type: 'png',
});
} catch (err) {
console.error(`Failed: ${urls[i]}`, err.message);
} finally {
await page.close(); // Libérer la mémoire entre les captures
}
}
} finally {
await browser.close();
}
}
Configuration Xvfb pour les captures d'écran
Faites correspondre la résolution Xvfb à votre plus grand viewport attendu :
# Profils 1080p standard
Xvfb :10 -screen 0 1920x1080x24 &
# Profils 1440p ou haute résolution
Xvfb :10 -screen 0 2560x1440x24 &
# Marge supplémentaire pour les captures pleine page
Xvfb :10 -screen 0 3840x2160x24 &
Utilisez toujours une profondeur de couleur de 24 bits. Définissez DISPLAY=:10.0 avant de lancer BotBrowser.
Vérification
Vérifiez la qualité des captures d'écran avec un test :
DISPLAY=:10.0 node -e "
const puppeteer = require('puppeteer-core');
(async () => {
const b = await puppeteer.launch({
executablePath: '/opt/botbrowser/chrome',
args: ['--bot-profile=/opt/profiles/profile.enc', '--window-size=1920,1080'],
headless: true,
defaultViewport: null,
});
const p = await b.newPage();
await p.goto('https://example.com', { waitUntil: 'networkidle0' });
await p.evaluate(() => document.fonts.ready);
await p.screenshot({ path: '/tmp/test-screenshot.png', type: 'png' });
console.log('Screenshot saved to /tmp/test-screenshot.png');
await b.close();
})();
"
Vérifiez le fichier de sortie pour :
- Des dimensions correctes correspondant au viewport
- Des couleurs complètes (pas de bandes ou d'artefacts de couleur)
- Tout le texte rendu avec les polices appropriées
- Le contenu complet de la page (pas de sections manquantes)
Bonnes pratiques
Définissez toujours defaultViewport: null dans Puppeteer. Cela préserve les dimensions du viewport du profil. Sans cela, Puppeteer utilise par défaut 800x600.
Attendez les polices avant de capturer. await page.evaluate(() => document.fonts.ready) empêche le rendu avec les polices de repli.
Utilisez networkidle0 pour le chargement initial, puis attendez des éléments spécifiques. Cette approche en deux étapes gère la plupart des pages de manière fiable.
Fermez les pages entre les captures dans les opérations par lots. Cela empêche l'accumulation mémoire qui dégrade la qualité pour les captures ultérieures.
Utilisez PNG pour la précision, JPEG pour le volume. PNG est sans perte mais plus volumineux. JPEG à qualité 85-90 est un bon équilibre pour la plupart des cas d'usage.
Faites correspondre la résolution Xvfb à votre viewport. Un affichage Xvfb plus petit que votre viewport coupe la zone de rendu.
Utilisez une profondeur de couleur de 24 bits. Toujours 1920x1080x24, jamais 1920x1080x16 ou 1920x1080x8.
Questions fréquentes
Pourquoi mes captures d'écran sont-elles vides ?
La cause la plus courante est la capture avant que la page ne finisse de charger. Utilisez waitUntil: 'networkidle0' et ajoutez des attentes explicites pour les éléments de contenu clés. Une autre cause est un affichage Xvfb manquant ou mal configuré sur Linux.
Pourquoi la capture d'écran a-t-elle la mauvaise taille ?
Dans Puppeteer, le viewport par défaut est 800x600. Définissez defaultViewport: null pour utiliser la taille réelle de la fenêtre. Pour BotBrowser, vérifiez aussi que --window-size ou --bot-config-window correspond à vos dimensions attendues.
Le ratio de pixels de l'appareil affecte-t-il la taille de la capture d'écran ?
Oui. Un viewport de 1920x1080 à 2x DPI produit une image de 3840x2160 pixels. C'est le comportement correct pour les captures d'écran haute résolution. Si vous voulez une sortie de 1920x1080 pixels, utilisez 1x DPI.
Puis-je capturer des captures d'écran avec des media queries CSS spécifiques ?
Oui. Utilisez page.emulateMediaType('print') pour la mise en page d'impression, ou injectez du CSS via page.addStyleTag() pour des conditions média personnalisées.
Comment gérer les pages avec défilement infini ?
Faites défiler jusqu'à la position souhaitée d'abord avec page.evaluate(() => window.scrollTo(0, 5000)), attendez que le contenu charge, puis capturez. Les captures d'écran pleine page sur les pages à défilement infini ne captureront que le contenu qui a été chargé.
Quelle est la taille maximale de capture d'écran ?
Chrome limite la surface de rendu. Les pages très longues (au-dessus d'environ 16 000 pixels de hauteur) peuvent produire des captures d'écran tronquées. Pour les pages extrêmement longues, capturez des sections et assemblez-les.
Comment comparer les captures d'écran pour les tests de régression visuelle ?
Utilisez des outils comme pixelmatch (package npm) ou resemblejs pour comparer deux fichiers PNG pixel par pixel. Définissez un seuil de tolérance pour tenir compte des différences mineures d'anticrénelage entre les exécutions.
Résumé
La capture d'écran fiable dans les environnements headless nécessite une attention à la configuration du viewport, au timing de chargement des pages, à la disponibilité des polices et à la configuration du serveur d'affichage. Avec BotBrowser, les valeurs de viewport et DPI du profil devraient guider votre configuration. Utilisez defaultViewport: null dans Puppeteer, attendez à la fois l'inactivité réseau et le chargement des polices avant la capture, et utilisez toujours une profondeur de couleur Xvfb de 24 bits.
Pour la configuration d'affichage, voir Headless Server Setup. Pour les pipelines de capture d'écran basés sur Docker, voir Docker Deployment Guide. Pour optimiser le volume de captures d'écran à grande échelle, voir Optimizing BotBrowser Performance.
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.