Ce qui rend Crawlee si utile, c’est que son orchestration de crawl gère à la fois l’analyse HTTP et l’exécution dans un navigateur. Une même URL peut donc donner des résultats différents selon le crawler choisi et le critère de disponibilité appliqué.
Sur la page publique Quotes to Scrape JS, CheerioCrawler n’a trouvé 0 citation cible, alors que PlaywrightCrawler en a trouvé 10 après avoir attendu .quote. Le cycle de crawl reste similaire, mais ce n’est pas juste un simple changement de classe en une ligne : le handler Cheerio utilisait $, tandis que le handler Playwright utilisait page, une attente explicite et une extraction côté navigateur.
Ce qu’est réellement Crawlee
Crawlee (le projet apify/crawlee, version 3.17.0) est une bibliothèque de web scraping et d’automatisation de navigateur pour Node.js et TypeScript. Elle prend en charge le crawl HTTP via Cheerio ou JSDOM, ainsi que le crawl dans un navigateur via Playwright ou Puppeteer. Le projet est publié sous Apache-2.0 ; vérifiez les obligations de notice et d’attribution avant toute redistribution.
Le bon modèle mental, c’est de séparer « récupérer la page » de « lire la page ». Un premier chemin télécharge le HTML brut sans exécuter JavaScript. L’autre lance Chromium et peut exécuter les scripts de la page, mais il dépend quand même d’un critère de disponibilité adapté et peut rater du contenu protégé par interaction, chargé à la demande, rendu via shadow DOM, bloqué par une API en échec ou filtré par un anti-bot. Crawlee expose des concepts de cycle de vie cohérents entre ces chemins, pas des primitives DOM interchangeables.
C’est ce point qu’il faut bien saisir avant d’écrire le moindre sélecteur, parce que le choix entre ces deux moteurs détermine si votre scraper renvoie des données… ou rien du tout sur un site donné.
Fonctionnalités clés : deux moteurs, une seule interface

CheerioCrawler récupère le HTML et l’analyse avec Cheerio ; PlaywrightCrawler pilote Chromium et peut prendre des captures d’écran. Les deux utilisent un requestHandler, exposent run(), et partagent des notions de crawl comme les queues et la découverte de liens. Le contexte du handler diffère : dans le test, la voie Cheerio extrayait via $, tandis que la voie navigateur utilisait page, waitForSelector et $$eval. La gestion de la queue et du cycle de vie peut rester familière, mais le code d’extraction peut demander un adaptateur ou une réécriture.
En coulisses, Crawlee fournit l’infrastructure qu’un crawl réel réclame. Un RequestQueue gère le stock des URL à visiter, élimine les doublons et suit ce qui a déjà été traité. enqueueLinks découvre et ajoute de nouvelles URL à la file d’attente (avec filtrage par sélecteur et par même hostname), ce qui permet au crawl de se propager tout seul. Un Dataset rassemble les enregistrements extraits en vue de l’export. Par défaut, Crawlee persiste tout ça dans un répertoire local storage/ sur disque — pratique pour reprendre un crawl, et un peu agaçant la première fois qu’on tombe sur un dossier storage/ qu’on n’a jamais demandé dans son projet (dans mon harness, j’ai redirigé ça vers un dossier temporaire et désactivé la persistance pour garder le test propre).
Pris séparément, ces éléments sont assez classiques. L’intérêt, c’est qu’ils sont partagés entre les deux moteurs : la queue, la découverte de liens et le dataset se comportent donc de la même façon que vous crawliez en HTTP ou via un navigateur. On apprend une seule API et on obtient deux stratégies de récupération.
Installation : le navigateur s’installe à part
L’installation testée n’incluait pas d’exécutable Chromium après l’ajout des packages.

npm install crawlee playwright s’est installé sans souci chez moi — 85 packages, 0 vulnérabilité, rien d’anormal. Si tu t’arrêtes là et que tu lances un CheerioCrawler, tout fonctionne, car le crawl HTTP n’a pas besoin d’un navigateur.
Dans cet environnement, Chromium devait être installé séparément avec npx playwright install chromium ; sans ça, PlaywrightCrawler ne pouvait pas démarrer. Le poids observé du navigateur était d’environ 82 MiB, mais les notes d’origine ne précisent pas s’il s’agissait de la taille de transfert ou de la taille sur disque. C’est donc une observation liée à la machine, pas une propriété fixe du produit. Les chemins de documentation et le comportement des packages peuvent évoluer, donc cet article n’affirme pas que cette omission est universelle ni définitivement non documentée.
Prévoyez donc l’installation en deux temps : d’abord les packages Node, puis le navigateur utilisé par la voie Playwright. Vérifiez à nouveau les instructions actuelles de Crawlee et de Playwright selon les versions et la plateforme que tu déploies.
En pratique : une même page, deux réponses très différentes

Le test principal a envoyé le même fichier de test rendu en JavaScript aux deux crawlers. L’URL et les champs cibles étaient partagés ; les primitives d’extraction, elles, ne l’étaient pas.
Sur le fichier local, CheerioCrawler a renvoyé 0 carte cible, car elles n’étaient pas présentes dans le HTML brut. PlaywrightCrawler a attendu #dynamic-products article.product-card, puis a renvoyé les 8 cartes attendues et capturé une capture d’écran. Ce résultat confirme que le fichier de test contenait bien les champs sélectionnés après cette attente ; il ne veut pas dire qu’un navigateur voit tous les états possibles d’une page. Les fichiers sources et la capture d’écran sont dans le dépôt de benchmark.

Sur la page publique Quotes to Scrape JS, CheerioCrawler a trouvé 0 citation cible et PlaywrightCrawler a attendu .quote avant d’en extraire 10. Cela confirme la même frontière HTTP-versus-navigateur sur une cible publique, même si la classe de crawler, le contexte du handler, la condition d’attente et la primitive d’extraction diffèrent entre les deux approches.
La conclusion utile est plus précise : validez les champs requis après la voie HTTP, puis passez à un crawler navigateur lorsque la réponse brute ne les contient pas. Le handler navigateur doit lui aussi attendre une condition liée à ces champs.
La voie HTTP a produit tous les enregistrements attendus sur les fixtures statiques contrôlées de catalogue et d’articles, a décodé les huit éléments attendus à partir d’une réponse JSON directe, a parcouru un graphe borné de 11 pages et a orienté une réponse 500 vers failedRequestHandler. Ce sont des vérifications de capacités distinctes, pas un score de précision unique. Face à la page publique Books to Scrape, le sélecteur configuré a renvoyé 20 produits comme test rapide.
| Test | Moteur | Résultat |
|---|---|---|
| Extraction statique : catalogue + pagination | CheerioCrawler | 12/12 produits attendus |
| Extraction d’article | CheerioCrawler | titre + 3/3 paragraphes |
| Transport : réponse JSON directe | CheerioCrawler | 8/8 produits attendus |
| Traversée : graphe de liens internes | CheerioCrawler | 11 pages, profondeurs {0:1, 1:3, 2:7} |
| Routage des erreurs : HTTP 500 | CheerioCrawler | le statut a atteint le handler d’échec |
| Rendu : fixture locale | CheerioCrawler | 0 carte cible dans le HTML brut |
| Rendu : fixture locale | PlaywrightCrawler | 8/8 après attente du sélecteur cible |
| Rendu : Quotes JS | CheerioCrawler | 0 citation cible dans le HTML brut |
| Rendu : Quotes JS | PlaywrightCrawler | 10 après attente du sélecteur cible |
Les timings complets et les chiffres par test sont disponibles dans results/crawlee-test-summary.json.
Soyons maintenant transparents sur les limites, parce qu’un passage sur une seule machine, en une seule exécution, a forcément ses angles morts et je ne vais pas faire comme si ce n’était pas le cas. Ce sont des temps mesurés, pas des benchmarks : une machine, une exécution chacun, donc considère le coût par page plus élevé de la voie navigateur comme « sensiblement plus lent que les exécutions Cheerio en sous-seconde », et non comme une valeur officielle. Et j’ai laissé de côté plusieurs éléments dans ce test : rotation de proxy, pools de sessions, exécutions à grande échelle sur des centaines ou des milliers de pages, persistance de RequestQueue et reprise après crash, le moteur Puppeteer, ainsi que l’ergonomie d’export de Dataset/KeyValueStore (ici, j’ai écrit les exports manuellement). Je peux attester du fonctionnement des deux moteurs et de la précision au niveau des fixtures. Je ne peux pas attester de la capacité à l’échelle ni du comportement anti-blocage, donc je ne vais pas le prétendre.
Ce qui est partagé, et ce qui doit changer

La surface commune, c’est l’orchestration du crawl. Les deux classes de crawler acceptent un requestHandler et exposent run(). Les queues, les métadonnées de requête, la découverte de liens, les hooks d’échec et les concepts de stockage peuvent être structurés de manière cohérente autour de l’un ou l’autre chemin d’exécution. Ça réduit la quantité d’infrastructure qu’une équipe doit réapprendre lorsqu’une cible exige un navigateur.
La surface d’accès à la page, en revanche, n’est pas commune. Un handler CheerioCrawler reçoit un accès orienté Cheerio, comme $, et peut travailler sur le corps de réponse sans navigateur. Le handler PlaywrightCrawler testé reçoit page ; il attend un sélecteur et effectue des évaluations sur le DOM du navigateur. Même lorsque les deux handlers produisent le même schéma d’enregistrement, ils y parviennent via des API différentes. Un adaptateur réutilisable pourrait masquer une partie de cette différence, mais ce harness n’en a ni implémenté ni démontré un.
Cette distinction compte pour les estimations. Changer la classe de crawler peut préserver la queue, le dataset et la politique d’URL, mais les sélecteurs, les vérifications de disponibilité, les captures d’écran, les interactions et la gestion des erreurs peuvent quand même changer. L’article traite donc l’« infrastructure de crawl partagée » comme le bénéfice vérifié et rejette la « migration en une ligne » comme promesse non étayée.
Un flux pragmatique pour choisir le moteur
Commence par la voie HTTP lorsque le HTML renvoyé ou une réponse JSON directe contient les champs requis. Définis un contrat de complétude — clés obligatoires, nombre minimum d’éléments ou sélecteur cible — et fais échouer explicitement le test quand il n’est pas respecté. Un tableau vide ne prouve pas que la page n’a pas de données ; dans les deux cas JavaScript ici, ça voulait juste dire que la représentation choisie ne contenait pas les éléments cibles.
| Condition cible | Démarrer avec | Basculer quand |
|---|---|---|
| Les champs requis sont dans le HTML renvoyé | CheerioCrawler | Les sélecteurs ou champs requis sont absents |
| Une réponse JSON reproductible contient les données | CheerioCrawler | La requête dépend d’un état accessible uniquement dans le navigateur |
| La page insère les éléments cibles après exécution | PlaywrightCrawler | Sans objet ; définissez une vérification de disponibilité propre à la cible |
| Le mélange des cibles est inconnu | HTTP d’abord avec validation de complétude | La validation échoue avec un résultat typé « représentation incomplète » |
Transfère cet échec typé vers un handler navigateur quand l’exécution devient nécessaire. Dans ce harness, la page locale attendait #dynamic-products article.product-card, tandis que la page publique des citations attendait .quote. Ces conditions font partie du contrat d’extraction. Un événement générique de chargement ne prouverait pas que les données applicatives sont bien arrivées, et ce test ne permet pas d’énoncer une règle d’attente universelle.
Après le basculement, garde le schéma de sortie stable même si les primitives DOM changent. Enregistre quel moteur a produit le résultat, quelle condition de disponibilité a été satisfaite et si la validation des champs requis a réussi. Ça rend le fallback HTTP-vers-navigateur observable, au lieu de transformer silencieusement les champs manquants en enregistrements acceptés.
Enfin, traite l’installation du navigateur et son coût d’exploitation comme des paramètres de déploiement. L’observation d’environ 82 MiB n’est utile qu’à titre d’ordre de grandeur local ; mesure précisément la version du navigateur, la plateforme, le comportement du cache et l’impact de l’image dans ton environnement. La rotation de proxy, les sessions, la persistance, la reprise après crash et la concurrence soutenue doivent encore être testées séparément avant que cette fixture puisse éclairer un choix à l’échelle de la production.
Avantages et inconvénients
Avantages :
- Les crawlers HTTP et navigateur partagent les concepts de cycle de vie tout en exposant des contextes d’extraction propres à chaque moteur.
- Extraction HTTP fiable à 100 % sur des catalogues statiques, des articles et des API JSON.
- Infrastructure commune aux deux moteurs :
RequestQueue,enqueueLinksavec contrôle de profondeur,Dataset. - La voie navigateur a exécuté les scripts de la fixture et récupéré tous les éléments cibles attendus dans les deux tests rendus en JavaScript.
- Gestion propre des erreurs — le HTTP 500 est remonté sans faire planter le crawl.
- Licence Apache-2.0 ; les utilisateurs en aval doivent vérifier les obligations de notice et d’attribution.
Inconvénients :
- Dans l’environnement testé, le moteur navigateur nécessitait une installation séparée de Chromium ; sans cela,
PlaywrightCrawlerne démarrait pas. - La voie HTTP ne peut pas exposer des éléments cibles absents du HTML brut ; sans validation de complétude, cela peut ressembler à tort à un résultat vide valide.
- La voie navigateur ajoute un binaire de navigateur et un coût local par page plus élevé dans cette exécution ; la taille et les temps varient selon la build et la plateforme.
- Les exécutions par défaut laissent un dossier
storage/sur le disque. - Uniquement Node/TypeScript — aucun intérêt si ta stack est en Python.
À qui cela convient — et qui devrait passer son chemin
Crawlee convient aux équipes Node ou TypeScript qui ont besoin de crawl HTTP et navigateur avec des concepts partagés de queue et de cycle de vie. Une approche pragmatique consiste à essayer d’abord le crawler HTTP, valider les champs requis, puis transmettre un échec typé de complétude à un handler navigateur avec une condition de disponibilité adaptée à la cible. Le code d’accès DOM du handler dépend du moteur, même si l’infrastructure de queue et de découverte de liens est partagée.
Mieux vaut revoir ses attentes, ou aller voir ailleurs, si tu travailles en Python (Crawlee est en Node/TS — il existe bien un port Python séparé, mais ce pack a testé la bibliothèque Node), si toutes tes cibles sont statiques et que tu préfères un scraper HTTP plus léger et spécialisé, ou si tu as besoin d’un comportement prouvé à grande échelle — rotation de proxy, pools de sessions, reprise après crash — que cette prise en main n’a pas couverts. Et si tu utilises PlaywrightCrawler, installe Chromium d’abord, sinon il ne démarrera tout simplement pas.
Alternatives, et où Thunderbit se situe
Crawlee est un logiciel open source que tu exécutes et maintiens toi-même. Il n’y a pas de frais par appel liés à un éditeur, mais le calcul navigateur, la bande passante, les proxies, le stockage, l’observabilité et l’ingénierie restent des coûts d’exploitation. Tu gères toi-même le choix du crawler, le binaire du navigateur, l’état du stockage et la logique de disponibilité.
Article connexe : test de scrapy-playwright.
Un service d’extraction managé transfère la responsabilité de l’acquisition et de la structuration du schéma à un fournisseur. Nous développons Thunderbit, mais nous ne l’avons pas exécuté sur ces fixtures ; cet article ne permet donc aucune comparaison de qualité, de latence, d’équivalence fonctionnelle ou de coût. La vraie question, c’est de savoir si ton équipe veut le contrôle intégré de Crawlee ou la frontière d’un service facturé à l’appel.
Autres revues de benchmark : la comparaison complète des scrapers open source, Playwright vs Puppeteer sur les mêmes pages et la revue de Scrapy sans navigateur et sans rejouer les requêtes.
Essayez Thunderbit pour l’extraction de données web
Verdict
Crawlee est un très bon candidat pour les équipes Node ou TypeScript qui veulent une orchestration de crawl partagée entre HTTP et exécution navigateur. Les handlers testés n’étaient pas interchangeables : passer à Playwright exigeait page, une attente du sélecteur cible et une extraction côté navigateur. Les comportements liés aux proxies, aux sessions, à la persistance, à la reprise et à grande échelle restent des questions ouvertes.
Essayez Thunderbit pour l’extraction de données web Get Started Free
FAQ
Quelle est la vraie différence entre les deux crawlers de Crawlee ?
CheerioCrawler récupère le HTML via HTTP et n’exécute pas JavaScript. PlaywrightCrawler pilote Chromium et peut exécuter les scripts de la page et prendre des captures d’écran, au prix d’un coût local par page plus élevé. Ils partagent les concepts de cycle de vie, mais pas le même contexte de handler : dans ce harness, la voie HTTP utilisait $, tandis que la voie Playwright utilisait page, une attente de sélecteur cible et une évaluation côté navigateur.
Pourquoi PlaywrightCrawler ne se lance-t-il pas après l’installation de Crawlee ?
Dans l’environnement testé, l’installation du package ne fournissait pas d’exécutable navigateur. L’installation de Chromium avec npx playwright install chromium a résolu l’échec au lancement. Le poids observé était d’environ 82 MiB, mais la mesure d’origine ne précisait pas s’il s’agissait du transfert ou du stockage disque ; il faut donc le remesurer pour ta plateforme et ta build.
CheerioCrawler peut-il scraper des pages rendues en JavaScript ?
Non, il ne peut pas exécuter le JavaScript de la page. En revanche, il peut interroger un point de terminaison JSON accessible côté client, comme le montre la fixture à réponse directe. Lorsque les données requises n’existent qu’après exécution dans le navigateur, utilise un crawler navigateur et une condition de disponibilité liée à ces champs.
Crawlee est-il fiable pour une extraction statique classique ? Sur les fixtures contrôlées, les handlers ont produit 12/12 produits de catalogue attendus, 3/3 paragraphes d’article attendus et 8/8 éléments JSON directs attendus. Ce sont des vérifications de complétude sur fixture, pas un score global de précision sur des sites non testés.
Crawlee est-il gratuit pour un usage commercial ? Il est publié sous Apache-2.0. Vérifie la licence actuelle dans le dépôt et les obligations de notice et d’attribution pour ta distribution.
Avant une adoption en production, teste les points laissés ouverts par cette fixture : concurrence répétée sur des pages représentatives, comportement des proxies et des sessions, reprise d’une queue persistante après interruption, nettoyage des processus navigateur et export du dataset en cas d’échec. Conserve avec ces résultats la version du navigateur et le chemin d’installation résolus. Les deux classes de crawler réduisent les divergences d’orchestration, mais elles n’éliminent pas la nécessité de vérifications de disponibilité spécifiques au moteur, de budgets de ressources et d’une gestion opérationnelle des échecs.


