Scraping headless : ce qui change vraiment
Comparez Chrome visible, headless unifié et headless shell pour le scraping. Consignez renderer, viewport et webdriver sans conclure pour un site.
BotBrowser Team
Vous voulez la documentation structurée pour Démarrage ?
Cet article fait partie de la bibliothèque éditoriale. Pour les étapes de configuration, la référence et les mises à jour continues, passez directement à la section docs.
BotBrowser permet d’utiliser Chrome avec un profil pour le scraping visible ou headless, mais le profil ne remplace pas le renderer de Chrome et ne garantit pas la réponse d’un site distant. Un script d’extraction peut renvoyer un contenu différent en mode Chrome visible et en mode headless, même lorsque les deux sessions visitent la même URL. La première question porte sur l’implémentation du navigateur et le mode de lancement ; la seconde consiste à vérifier si le viewport, l’échelle de l’appareil, les polices, le stockage ou l’état de la fixture ont changé en même temps. Ce sont des causes distinctes, et non des synonymes pour désigner un navigateur visible ou invisible.
Pour comparer des exécutions de scraping de façon contrôlée, distinguez trois cas : une fenêtre Chrome visible, le mode headless unifié de Chrome et le headless shell facultatif de Puppeteer. Chrome décrit le mode headless unifié comme utilisant l’implémentation normale de Chrome ; le shell est un exécutable séparé. Aucune de ces descriptions ne promet des sorties identiques ni ne prédit qu’un service distant acceptera une requête. Une fixture locale permet de voir quelles valeurs ont changé dans une configuration déclarée.
Résumé
headless: falseouvre une fenêtre visible ;headless: truesélectionne le mode unifié dans Puppeteer actuel ;headless: 'shell'sélectionne la compilation shell distincte de Puppeteer.- Gardez fixes la version du navigateur, la fixture, le viewport, l’échelle de l’appareil, le stockage et les flags de lancement avant d’attribuer une différence au mode.
- Consignez
window.innerWidth,screen.width,window.devicePixelRatio,navigator.webdriveret la version du navigateur comme des observations, pas comme une note d’identité ou d’acceptation. - Utilisez une fixture locale que vous contrôlez et ne changez qu’une variable à la fois. Une différence visible par la page ne prouve pas qu’un détecteur tiers a accepté ou refusé l’exécution.
Sommaire
- Trois modes d’exécution
- Ce qu’une page peut lire
- Un diff contrôlé entre deux modes
- Classer une différence
- Quand la comparaison ne permet pas de conclure
- Conclusion pratique
Trois modes d’exécution
« Headless » décrit la façon dont un navigateur est lancé, pas un moteur de rendu universel. Le binaire, la version du navigateur, les flags de lancement, le système d’exploitation, le serveur d’affichage, le viewport, les polices et le profil restent déterminants. Une comparaison utile nomme le mode et l’exécutable au lieu de ne conserver qu’une valeur booléenne.
Chrome avec fenêtre visible
Dans une exécution visible, Chrome crée une fenêtre de premier niveau affichée à l’écran. Le gestionnaire de fenêtres et l’environnement d’affichage peuvent influer sur les dimensions extérieures, la taille d’écran disponible, la mise à l’échelle, le focus et l’état de visibilité. Un harness de test peut définir le viewport de la page indépendamment de la fenêtre extérieure ; « visible » ne signifie donc pas que la page reçoit les dimensions du moniteur physique.
Dans Puppeteer, headless: false demande le mode visible. La page peut lire le viewport intérieur avec window.innerWidth et window.innerHeight ; screen.width et screen.height décrivent les dimensions d’écran exposées au document. Ces valeurs répondent à des questions différentes. Un viewport fixe peut stabiliser la première paire alors que l’affichage ou le gestionnaire de fenêtres fait varier la seconde.
Chrome headless unifié
Le mode headless actuel de Chrome utilise l’implémentation normale du navigateur Chrome plutôt que l’ancienne implémentation headless séparée. La documentation de Chrome décrit ce changement à partir de Chrome 112. Il s’agit d’une limite d’implémentation : cela ne signifie ni qu’une session headless possède une fenêtre de bureau visible, ni que toutes les valeurs accessibles à la page doivent correspondre à celles d’une session visible sur n’importe quel hôte.
Puppeteer utilise headless: true pour ce mode unifié. Un lancement en ligne de commande peut également demander le mode headless. L’exécutable et sa version exacte restent importants : un ancien paquet d’automatisation, un binaire épinglé ou un flag personnalisé peut changer ce qui a réellement été lancé. Consignez browser.version() ou la version négociée équivalente avec le mode, au lieu de supposer que le paquet installé a sélectionné le navigateur attendu.
Headless shell
Puppeteer propose aussi headless: 'shell', qui sélectionne l’exécutable séparé chrome-headless-shell. Ce n’est pas simplement un autre nom pour Chrome headless unifié. La documentation de Chrome décrit le shell comme un binaire headless distinct, et le guide Puppeteer documente son sélecteur de mode séparé. Une comparaison shell-visible peut donc modifier à la fois le mode de lancement et l’implémentation de l’exécutable si l’expérience ne consigne pas les deux.
Cette option est propre à Puppeteer ; d’autres bibliothèques d’automatisation peuvent utiliser d’autres noms ou ne pas proposer le shell avec la même API. Ne traduisez pas une option chaîne de Puppeteer en réglage Playwright sans consulter la documentation actuelle de cette bibliothèque. Pour la configuration du navigateur, et non la sémantique des modes, le guide d’intégration Puppeteer et les notes de déploiement headless traitent de questions opérationnelles distinctes.
| Libellé d’exécution | Sélecteur Puppeteer | Implémentation à consigner | Ce que le libellé ne prouve pas |
|---|---|---|---|
| Fenêtre visible | headless: false | Exécutable et version de Chrome | Que la sortie de page corresponde à un véritable appareil |
| Headless unifié | headless: true | Implémentation normale de Chrome et version | Que la page ne puisse pas distinguer son environnement |
| Headless shell | headless: 'shell' | Exécutable et version de chrome-headless-shell | Que shell et Chrome normal exposent un comportement identique |
Chrome 132 a rendu l’ancienne implémentation headless disponible sous la forme d’un binaire shell autonome. Les numéros de version ont donc leur place dans le relevé : les noms de mode peuvent rester proches alors que l’exécutable derrière un réglage d’automatisation change. Lors d’une mise à niveau de runner, consultez la documentation Chrome Headless et le guide des modes headless de Puppeteer ; ne déduisez pas l’implémentation sélectionnée d’un flag copié depuis un ancien article.
Ce qu’une page peut lire
Une page ne reçoit pas de propriété standard universelle isHeadless. Elle lit des API du navigateur, des résultats de mise en page et des comportements exposés par l’environnement configuré. Une fixture utile consigne quelques champs concrets et garde leurs significations distinctes au lieu de les réduire à une « empreinte headless ».
window.innerWidth et window.innerHeight décrivent le viewport de mise en page en pixels CSS. screen.width et screen.height décrivent les dimensions d’écran exposées au document. window.devicePixelRatio relie les pixels CSS aux pixels de l’appareil pour la configuration d’affichage courante. Une page responsive peut choisir une autre mise en page quand le viewport change, même si l’implémentation du navigateur et l’URL restent identiques. Les références MDN de innerWidth et de devicePixelRatio précisent le sens de ces API ; elles n’affirment pas que le seul mode headless détermine leurs valeurs.
navigator.userAgent et, lorsqu’il est disponible, navigator.userAgentData exposent des informations d’identification du navigateur soumises aux règles de réduction et de disponibilité. Ces chaînes ne permettent pas d’étiqueter de manière fiable une session comme visible ou headless. Une valeur User-Agent peut rester identique alors que le viewport ou le rendu change ; le code de page ne devrait pas la traiter comme une preuve d’appareil physique.
navigator.webdriver est un signal d’automatisation standardisé par WebDriver. MDN décrit les conditions dans lesquelles un navigateur indique cet état d’automatisation ; Chrome peut l’exposer dans des configurations d’automatisation comprenant l’exécution headless. Ce n’est pas un classificateur complet des modes : une valeur true signifie que le navigateur signale un contrôle WebDriver, pas qu’un site particulier a rendu un verdict. La référence Navigator.webdriver de MDN est la source appropriée pour le contrat de la propriété. Une comparaison distincte des signaux headless explique comment consigner plusieurs surfaces visibles du navigateur sans traiter un champ isolé comme un résultat de détection.
D’autres sorties dépendent de plus que du mode de lancement. La disponibilité des polices peut modifier les retours à la ligne et les dimensions des éléments. La langue et le fuseau horaire peuvent changer les textes formatés. Un cookie stocké peut faire apparaître un écran de connexion plutôt qu’un tableau de données. Le routage réseau peut changer la réponse serveur ou les délais. Une exécution de scraping qui modifie plusieurs de ces facteurs à la fois ne peut pas attribuer la différence de DOM obtenue au mode headless.
L’environnement de déploiement constitue un autre axe indépendant. La configuration de serveur headless de BotBrowser documente ses exigences de profil et d’affichage virtuel pour ce produit. La référence de cohérence d’automatisation décrit les valeurs du navigateur fondées sur un profil. Ce sont des détails de fonctionnement de BotBrowser, pas des affirmations sur l’architecture de Chrome en amont ni une promesse que toute page reçoit les mêmes valeurs dans chaque mode.
Un diff contrôlé entre deux modes
Utilisez une page que vous possédez ou que vous êtes autorisé à inspecter. Une fixture locale évite de mélanger les observations sur le mode de lancement avec l’autorisation, les limites de requêtes, les défis, les changements de contenu, les règles du service ou l’état d’un site distant. L’exemple ci-dessous attend une fixture à http://127.0.0.1:4173/fixture.html ; il n’a pas été exécuté ici et ne contient aucun résultat mesuré.
Exécutez un mode par processus. Utilisez la même installation Puppeteer, le même exécutable, la même version du navigateur, la même page locale, la même politique de profil, le même viewport, la même échelle d’appareil, la même langue et le même état de stockage. Pour les deux premières exécutions, le nom du mode est la seule variable intentionnelle. Une exécution shell constitue une comparaison distincte puisque son exécutable diffère.
import puppeteer from 'puppeteer';
const modeName = process.argv[2];
const modes = {
headed: false,
headless: true,
shell: 'shell',
};
if (!Object.hasOwn(modes, modeName)) {
throw new Error('Choose one mode: headed, headless, or shell');
}
const fixture = 'http://127.0.0.1:4173/fixture.html';
const browser = await puppeteer.launch({
headless: modes[modeName],
defaultViewport: {
width: 1365,
height: 768,
deviceScaleFactor: 1,
},
});
try {
const page = await browser.newPage();
await page.goto(fixture, { waitUntil: 'domcontentloaded', timeout: 10000 });
const pageFields = await page.evaluate(() => ({
origin: location.origin,
userAgent: navigator.userAgent,
webdriver: navigator.webdriver,
viewport: { width: innerWidth, height: innerHeight },
screen: { width: screen.width, height: screen.height },
devicePixelRatio,
visibilityState: document.visibilityState,
}));
process.stdout.write(
`${JSON.stringify(
{
mode: modeName,
browserVersion: await browser.version(),
pageFields,
},
null,
2
)}\n`
);
} finally {
await browser.close();
}
Enregistrez le même script sous mode-diff.mjs, puis capturez chaque exécution séparément :
node mode-diff.mjs headed > headed.json
node mode-diff.mjs headless > headless.json
node mode-diff.mjs shell > shell.json
diff -u headed.json headless.json
Le code imprime un relevé, pas un résultat de réussite ou d’échec. Il ne contacte volontairement aucun domaine tiers, ne compare aucun compte et n’affirme pas qu’un mode est accepté. Une sortie non nulle ou un fichier JSON absent indique un problème d’exécution ; des valeurs différentes sont des observations à classer. Pour une comparaison stricte entre visible et headless unifié, comparez les deux premiers fichiers et vérifiez que browserVersion correspond. Traitez la sortie shell comme une comparaison d’implémentation séparée et conservez sa version dans les notes.
Les valeurs de viewport de l’exemple sont des entrées fixes, pas la preuve d’un viewport universel. defaultViewport définit le viewport de page Puppeteer ; il ne contrôle pas toutes les dimensions de fenêtre du système d’exploitation. Le script consigne les dimensions de l’écran et l’échelle de l’appareil parce qu’elles peuvent révéler un changement de l’hôte. Si une fixture exige un viewport précis pour reproduire une branche responsive, conservez cette valeur pour chaque exécution et notez-la à côté des fichiers JSON.
Pour une fixture locale avec des données dynamiques, rendez la page de test déterministe avant de comparer les modes. Épinglez la version de la fixture, utilisez des ressources locales statiques, figez l’état de l’application et évitez une API en direct dont la réponse peut changer entre deux captures. Si une police est chargée sur le réseau, un écart de délai peut modifier la mise en page alors que le renderer est inchangé. Conservez la révision de la source de la page de test et l’ensemble des ressources avec le relevé de capture afin qu’une future mise à niveau du navigateur ne soit pas comparée à une autre fixture.
Classer une différence
Commencez par la version du navigateur et la configuration de lancement consignées. Si elles ne correspondent pas à la comparaison prévue, il ne s’agit pas d’une comparaison contrôlée des modes. Inspectez ensuite le viewport, les dimensions d’écran et l’échelle de l’appareil. Un innerWidth différent peut sélectionner un autre breakpoint CSS ; un devicePixelRatio différent peut modifier la rastérisation et la résolution du canvas de rendu. Ces résultats expliquent une différence de page par des entrées concrètes, pas par une étiquette headless générique.
Séparez ensuite trois catégories de résultats :
| Catégorie | Exemple | Diagnostic suivant |
|---|---|---|
| Différence d’entrée déclarée | L’exécution shell indique une autre version ou un autre exécutable | Conservez-la comme comparaison de binaires distincte ; ne l’attribuez pas seulement à la visibilité |
| Différence médiée par l’environnement | Les dimensions d’écran, les polices, la langue ou le stockage changent | Alignez l’entrée ou consignez la différence comme partie de l’environnement |
| Différence observée par la page | Une fixture locale lit une autre valeur de navigator.webdriver ou choisit une autre mise en page | Vérifiez le contrat de l’API et la configuration du navigateur ; ne déduisez pas la décision d’un détecteur distant |
Par exemple, un changement de navigator.webdriver doit être rattaché au contrôle d’automatisation et à la configuration de lancement du navigateur. La propriété indique un état lié à WebDriver ; elle ne dit pas si le scraping est autorisé ou si un site a accepté la session. Un changement de innerWidth doit être comparé au viewport de page configuré avant d’examiner le CSS. Un changement de screen.width signale une configuration d’écran exposée à la page et ne doit pas être renommé en identité d’appareil physique.
La comparaison des modes ne peut pas non plus isoler le comportement réseau. Deux exécutions peuvent recevoir un balisage différent parce que la fixture serveur, les cookies, le moment des requêtes ou le contenu en cache ont changé. Comparez la réponse de la fixture et l’état de sa source avant d’attribuer une différence DOM à Chrome. Si un site distant est concerné, respectez sa politique d’accès publiée et séparez les réponses serveur des lectures d’API navigateur ; ce texte ne fournit pas de méthode pour contourner les défis ou les contrôles d’accès.
Si l’objectif est de comprendre comment un détecteur nommé regroupe les observations, c’est une question différente du choix d’un mode de lancement pour le scraping. La page sur les couches de détection headless de CreepJS couvre les catégories de signaux publiées, tandis que la cohérence des profils examine les valeurs fondées sur un profil. Aucune ne transforme une comparaison locale en prédiction pour un service qui n’a pas été mesuré.
Quand la comparaison ne permet pas de conclure
La comparaison ne permet pas de conclure lorsque l’exécutable réel est inconnu, que les versions diffèrent, que la fixture a changé ou que les deux exécutions n’ont pas utilisé le même viewport et le même état. Elle ne permet pas non plus de conclure lorsqu’on ne conserve qu’une capture d’écran sans métadonnées de lancement : une image montre des pixels, mais pas le binaire, les flags, le profil, les polices, la langue ou la configuration d’affichage qui les ont produits.
Une observation isolée ne démontre pas qu’un comportement appartient à tous les navigateurs headless. Les versions évoluent. Puppeteer et Chrome peuvent suivre des calendriers de mise à jour différents. Le réglage headless: true n’a de sens qu’avec la version de bibliothèque qui l’interprète et pour l’exécutable qu’elle lance. Épinglez ces dépendances pour une comparaison reproductible, puis relancez la fixture contrôlée après une mise à niveau volontaire.
Ne combinez pas des changements de mode, de viewport, de profil, de langue et de réseau dans un unique test « avant/après ». Si plusieurs variables bougent, plusieurs causes peuvent expliquer le résultat de page. Construisez plutôt une petite matrice : visible et headless unifié avec le même exécutable, la même version et le même viewport ; puis une ligne shell distincte avec son exécutable réel ; enfin, si nécessaire, un second test de viewport avec le mode inchangé. Chaque paire répond à une question limitée.
Le comportement documenté des profils BotBrowser appartient à cette matrice comme configuration produit, pas comme mode Chrome en amont. Un profil peut fournir des valeurs navigateur documentées, et la page de configuration serveur décrit des contraintes de déploiement. Cela ne signifie ni que l’architecture du renderer change, ni que tous les signaux de l’hôte deviennent identiques, ni qu’un site renverra le même contenu. La frontière entre cohérence fondée sur un profil et sortie visible/headless garde ces questions distinctes.
FAQ
Chrome headless est-il toujours détectable ?
Il n’existe pas de booléen universel pris en charge qui réponde à cette question pour toutes les pages et versions du navigateur. Une page peut lire des API et des résultats de mise en page, et une configuration d’automatisation peut exposer un état lié à WebDriver. Une lecture donnée constitue une preuve pour cette exécution, pas une garantie que tous les sites utilisent les mêmes champs ou tirent la même conclusion. Le résultat défendable est un relevé de la version, du mode de lancement, des entrées, de la fixture et des champs observés.
Chrome headless produit-il la même page que Chrome visible ?
L’implémentation headless unifiée de Chrome partage l’implémentation normale de Chrome, mais partager une implémentation n’égalise pas toutes les entrées d’environnement. Une fenêtre de bureau visible, un affichage virtuel, les polices, la pile graphique, le viewport, l’échelle de l’appareil et le profil peuvent différer. Comparez le même exécutable sur une fixture contrôlée avant d’attribuer un changement au mode de lancement. Ne promettez pas des pixels ou un DOM identiques entre systèmes d’exploitation.
headless: 'shell' est-il juste une autre écriture de headless: true ?
Non. Puppeteer documente le sélecteur shell comme un exécutable headless shell séparé, tandis que true sélectionne Chrome headless unifié. Les versions du paquet et les options d’installation déterminent le binaire disponible. Consignez la version navigateur de chaque exécution ; ne fusionnez pas les résultats shell avec une référence headless unifiée sans documenter ce changement d’implémentation.
Un scraper doit-il passer en mode visible quand les données diffèrent ?
Identifiez d’abord le champ qui a changé et vérifiez les entrées contrôlées. Si la différence provient du viewport, des polices, du stockage ou de l’état de page, changer de mode peut masquer la cause au lieu de la corriger. Si un service distant change sa réponse, respectez ses règles d’accès documentées et examinez séparément la réponse serveur. Un changement de mode de lancement n’est pas une méthode de contournement des contrôles d’accès et ne démontre pas que l’extraction est autorisée ou fiable.
Sources
- Chrome Headless
- Nouveau mode Headless de Chrome
- Puppeteer : modes Headless
- BotBrowser : configuration d’un serveur headless
- BotBrowser : cohérence d’automatisation
- MDN : Navigator.webdriver
- MDN : Window.innerWidth
- MDN : Window.devicePixelRatio
Conclusion pratique
Choisissez un mode de lancement adapté au déploiement et à la charge, puis consignez ce qui a réellement été lancé. Comparez visible et headless unifié sur la même fixture locale, avec une version, un viewport, une politique de profil et un état identiques ; traitez shell comme un exécutable distinct. Le diff obtenu peut expliquer des changements visibles par la page dans cette configuration. Il ne certifie ni identité, ni autorisation, ni réponse d’un site.
Cet article fait partie du guide : Débuter avec l'automatisation du navigateur : carte initiale
Articles Connexes
Chrome headless nouveau et ancien : shell, mode et écarts
DémarrageSelenium sans fenêtre : ChromeOptions et écarts
DéploiementSignaux headless et avec interface : méthode de comparaison
DémarrageScraping avec Puppeteer : options et observations
PlateformeCohérence des profils entre navigateurs headless et headed
DémarrageScraping avec Playwright : navigateur et profil
Exécutez la même vérification dans BotBrowser
Chargez un profil, gardez dans le navigateur les signaux que vous venez de lire, puis confirmez le résultat sur votre propre machine avant le déploiement.