In this Article
Ce guide explique comment faire du web scraping avec JavaScript en utilisant Node.js, l’environnement d’exécution que la plupart des ingénieurs utilisent pour collecter des données publiques à grande échelle. Apprendre à faire du web scraping avec JavaScript revient à choisir l’outil adapté à chaque page : un client HTTP léger pour le HTML statique ou un navigateur headless pour le contenu chargé par des scripts.
Vous trouverez ci-dessous du code Node.js fonctionnel et exécutable pour les deux cas, ainsi que la configuration du projet, l’export structuré vers JSON et CSV, la pagination, les tentatives répétées avec backoff, la limitation de débit, l’authentification proxy, l’interception XHR, et les questions d’éthique et de blocage qui déterminent si un scraper continue de fonctionner dans la durée.
DataImpulse est un fournisseur de proxies éthiques qui propose plus de 90 millions d’adresses IP résidentielles, mobiles et datacenter dans 195 pays. Son modèle de paiement à l’usage démarre à 1 dollar par Go et le trafic n’expire pas, et est utilisé pour le web scraping, la vérification publicitaire, la surveillance des prix, l’étude de marché et la gestion multi-comptes.
Points clés
- Deux chaînes d’outils : Les pages statiques n’ont besoin que d’un client HTTP et un parseur comme fetch et cheerio, tandis que les pages dynamiques qui affichent leur contenu dans le navigateur ont besoin d’un moteur headless comme Puppeteer ou Playwright.
- Meilleur type de proxy : les proxies résidentiels rotatifs, qui s’appuient sur de véritables IP de particuliers et passent mieux les systèmes de détection.
- Prix : à partir de 1 dollar par Go, paiement à l’usage, avec trafic sans expiration et sans abonnement.
- Couverture : plus de 90M d’IP issues de sources éthiques dans 195 pays.
- Fiabilité : taux de réussite de 99.51%, noté 4.8 sur 5 sur G2.
- Protocoles et ciblage : HTTP, HTTPS et SOCKS5, avec ciblage par pays inclus.

Qu’est-ce que le web scraping avec JavaScript ?
Le web scraping avec JavaScript est le processus consistant à demander des pages web de manière programmatique et à en extraire des données structurées à l’aide de JavaScript, généralement exécuté dans l’environnement Node.js plutôt que dans un onglet de navigateur.
JavaScript est un choix naturel car le langage modélise déjà le Document Object Model, ce qui rend la sélection d’éléments familière aux développeurs front-end. En pratique, un scraper fait trois choses : il récupère une page via HTTP, il analyse le balisage renvoyé pour former une arborescence interrogeable, et il extrait les champs qui vous intéressent, comme les titres, les prix ou les liens. La complexité vient de la façon dont la page cible livre ses données, c’est pourquoi le choix de l’outil compte davantage que l’analyse elle-même. Les pages statiques envoient leurs données dans la première réponse HTML, tandis que les pages dynamiques les assemblent dans le navigateur après le chargement, et les deux cas nécessitent des chaînes d’outils différentes.
Comment configurer un projet de scraping Node.js ?
Créez un dossier de projet, initialisez-le avec npm, et installez les bibliothèques nécessaires avant d’écrire le moindre code de scraping.
Node.js 18 et versions ultérieures incluent un fetch global, vous n’avez donc pas besoin d’une bibliothèque HTTP pour de simples requêtes GET, bien que de nombreuses équipes préfèrent encore axios pour ses intercepteurs et sa gestion des proxies. Les commandes ci-dessous créent la structure d’un projet et installent cheerio pour l’analyse, axios comme client alternatif, p-limit pour le contrôle de la concurrence, ainsi que Puppeteer et Playwright pour les pages dynamiques :
mkdir js-scraper && cd js-scraper
npm init -y
npm install cheerio axios p-limit
npm install puppeteer playwright
# enable modern import syntax
npm pkg set type=module
Définir type=module vous permet d’utiliser des instructions import. Si vous préférez CommonJS, conservez la valeur par défaut et utilisez require à la place. Les exemples de ce guide utilisent la syntaxe import, mais chacun correspond directement à un appel require. Une fois l’installation terminée, vous disposez de tout ce qu’il faut pour le scraping statique comme dynamique dans un seul projet.
Comment scraper une page statique avec fetch et cheerio ?
Pour les pages statiques, téléchargez le HTML avec la fonction fetch intégrée et analysez-le avec cheerio, qui fournit une API de type jQuery côté serveur.
Les pages statiques renvoient leur contenu directement dans la réponse HTML initiale, donc aucun rendu de navigateur n’est nécessaire. Cette approche est rapide et peu coûteuse car vous ne téléchargez que le balisage, pas les images, les polices ou les scripts. L’exemple ci-dessous récupère une page, la charge dans cheerio et extrait chaque fiche produit :
import * as cheerio from 'cheerio';
async function scrape(url) {
const res = await fetch(url, {
headers: { 'User-Agent': 'Mozilla/5.0 (compatible; MyScraper/1.0)' }
});
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
const html = await res.text();
const $ = cheerio.load(html);
const items = [];
$('.product').each((i, el) => {
items.push({
title: $(el).find('h2').text().trim(),
price: $(el).find('.price').text().trim(),
link: $(el).find('a').attr('href')
});
});
return items;
}
console.log(await scrape('https://example.com/catalog'));
Si vous préférez axios, l’analyse reste identique : seule la requête change. Axios lève automatiquement une erreur sur les codes de statut hors 2xx et simplifie la configuration du proxy, c’est pourquoi de nombreux scrapers en production l’utilisent :
import axios from 'axios';
import * as cheerio from 'cheerio';
const { data: html } = await axios.get('https://example.com/catalog', {
headers: { 'User-Agent': 'Mozilla/5.0 (compatible; MyScraper/1.0)' },
timeout: 15000
});
const $ = cheerio.load(html);
const titles = $('.product h2').map((i, el) => $(el).text().trim()).get();
console.log(titles);
Avant de pointer un scraper vers un site, il vaut la peine de confirmer que la cible l’autorise. Notre guide pour vérifier si un site autorise le scraping passe en revue le robots.txt et les conditions d’utilisation.
Comment extraire des données structurées vers JSON et CSV ?
Rassemblez chaque enregistrement dans un tableau d’objets simples, puis écrivez ce tableau dans un fichier JSON, ou aplatissez-le en lignes séparées par des virgules pour un fichier CSV.
Les données extraites ne deviennent utiles qu’une fois stockées dans un format prévisible. JSON est la cible la plus simple car un tableau JavaScript se sérialise directement. L’extrait ci-dessous enregistre les éléments extraits sur le disque avec le module de système de fichiers intégré :
import { writeFile } from 'node:fs/promises';
const items = await scrape('https://example.com/catalog');
await writeFile('products.json', JSON.stringify(items, null, 2), 'utf-8');
console.log(`Saved ${items.length} records to products.json`);
Le CSV convient mieux lorsque les données alimentent un tableur ou un entrepôt de données. Vous pouvez le construire sans bibliothèque en échappant les guillemets et en joignant les champs, bien que cet échappement est important, car les prix et les titres peuvent contenir des virgules :
import { writeFile } from 'node:fs/promises';
function toCsv(rows) {
const headers = Object.keys(rows[0]);
const escape = (v) => `"${String(v ?? '').replace(/"/g, '""')}"`;
const lines = [headers.join(',')];
for (const row of rows) {
lines.push(headers.map((h) => escape(row[h])).join(','));
}
return lines.join('\n');
}
const items = await scrape('https://example.com/catalog');
await writeFile('products.csv', toCsv(items), 'utf-8');
Gardez vos noms de champs stables d’une exécution à l’autre afin que les outils en aval ne cessent pas de fonctionner lorsque la mise en page d’une page change légèrement. Si vous devez aussi récupérer des ressources binaires, consultez notre guide sur la façon de scraper des images d’un site web.
Comment scraper plusieurs pages avec la pagination ?
Parcourez les URL paginées du site ou suivez le lien vers la page suivante, en scrapant chaque page et en vous arrêtant lorsqu’il n’y a plus de résultats.
La plupart des catalogues répartissent leurs résultats sur des pages numérotées à l’aide d’un paramètre de requête comme ?page=2, ou exposent un lien suivant. Le modèle le plus sûr lit chaque page, collecte ses enregistrements, puis cherche la page suivante et s’arrête lorsqu’elle est absente. L’exemple ci-dessous parcourt des pages numérotées jusqu’à ce qu’une page ne renvoie aucun élément :
import * as cheerio from 'cheerio';
async function scrapeAll(base) {
const all = [];
for (let page = 1; page <= 50; page++) {
const res = await fetch(`${base}?page=${page}`, {
headers: { 'User-Agent': 'Mozilla/5.0 (compatible; MyScraper/1.0)' }
});
if (!res.ok) break;
const $ = cheerio.load(await res.text());
const items = $('.product').map((i, el) => ({
title: $(el).find('h2').text().trim(),
price: $(el).find('.price').text().trim()
})).get();
if (items.length === 0) break;
all.push(...items);
await new Promise((r) => setTimeout(r, 1000));
}
return all;
}
console.log((await scrapeAll('https://example.com/catalog')).length);
La pause d’une seconde entre les pages et la limite stricte de 50 itérations, qui empêche une boucle incontrôlée si le site ne renvoie jamais de page vide. Lorsque le contenu est chargé par défilement plutôt que par des pages numérotées, il vous faut un navigateur headless, ce que couvre la section Puppeteer ci-dessous.
Comment gérer les nouvelles tentatives, le backoff et la limitation de débit ?
Enveloppez chaque requête dans un utilitaire de nouvelle tentative qui attend de plus en plus longtemps après chaque échec et limitezz le nombre de requêtes exécutées en même temps pour rester dans les limites acceptées par le site.
Les réseaux ne sont pas fiables et les serveurs renvoient des erreurs transitoires, donc un scraper qui abandonne au premier échec perd des données inutilement. Un mécanisme de nouvelles tentatives avec backoff exponentiel attend progressivement plus longtemps entre les tentatives, ce qui laisse aussi le temps à un serveur limité en débit de récupérer :
async function fetchWithRetry(fn, retries = 4, delay = 1000) {
for (let attempt = 1; attempt <= retries; attempt++) {
try {
return await fn();
} catch (err) {
if (attempt === retries) throw err;
const wait = delay * 2 ** (attempt - 1);
console.warn(`Attempt ${attempt} failed, retrying in ${wait}ms`);
await new Promise((r) => setTimeout(r, wait));
}
}
}
const html = await fetchWithRetry(async () => {
const res = await fetch('https://example.com/catalog');
if (res.status === 429) throw new Error('Rate limited');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.text();
});
Le backoff gère les requêtes individuelles, mais vous devez aussi contrôler leur nombre d’exécutions parallèles. Envoyer des centaines de requêtes concurrentes dégrade le serveur cible et rend votre trafic suspect. La bibliothèque p-limit plafonne la concurrence afin qu’un nombre fixe de workers traite une file d’URL :
import pLimit from 'p-limit';
const limit = pLimit(5); // at most 5 requests in flight
const urls = Array.from({ length: 100 }, (_, i) =>
`https://example.com/item/${i + 1}`
);
const results = await Promise.all(
urls.map((url) =>
limit(() => fetchWithRetry(async () => {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.text();
}))
)
);
console.log(`Fetched ${results.length} pages with a cap of 5 in parallel`);
Ensemble, le backoff et un plafond de concurrence maintiennent votre empreinte raisonnable et votre taux de réussite élevé. Pour une liste de contrôle plus large, consultez notre guide des bonnes pratiques de web scraping.
Comment scraper des pages dynamiques avec Puppeteer ?
Pour les pages dynamiques qui génèrent leur contenu avec JavaScript après le chargement, pilotez Chrome headless avec Puppeteer, attendez le sélecteur cible, et lisez le DOM entièrement rendu.
De nombreux sites modernes renvoient une structure HTML presque vide, puis récupèrent les données via des requêtes en arrière-plan. Un client HTTP ne voit rien d’utile, il vous faut donc un moteur qui exécute le JavaScript de la page elle-même. Puppeteer pilote Chrome headless et peut attendre des éléments, cliquer et faire défiler. L’exemple ci-dessous attend un sélecteur, puis fait défiler la page à plusieurs reprises pour déclencher le chargement par défilement infini avant d’extraire les données depuis le navigateur :
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
await page.setUserAgent('Mozilla/5.0 (compatible; MyScraper/1.0)');
await page.goto('https://example.com/feed', { waitUntil: 'networkidle2' });
// wait until the first batch of cards is present
await page.waitForSelector('.product');
// infinite scroll: scroll to the bottom until height stops growing
let previousHeight = 0;
for (let i = 0; i < 20; i++) {
const height = await page.evaluate('document.body.scrollHeight');
if (height === previousHeight) break;
previousHeight = height;
await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
await new Promise((r) => setTimeout(r, 1500));
}
const items = await page.evaluate(() =>
Array.from(document.querySelectorAll('.product')).map((el) => ({
title: el.querySelector('h2')?.innerText.trim(),
price: el.querySelector('.price')?.innerText.trim()
}))
);
console.log(`Extracted ${items.length} items`);
await browser.close();
La boucle s’arrête dès que la hauteur de la page cesse d’augmenter, ce qui signale qu’aucun nouveau contenu ne se charge, et la limite stricte de 20 défilements empêche une boucle infinie. Pour en savoir plus sur ce type de cible, consultez notre guide sur la façon de scraper des pages web dynamiques.
Comment utiliser Playwright comme alternative à Puppeteer ?
Playwright fonctionne presque de façon identique à Puppeteer, mais offre une prise en charge native pour Chromium, Firefox et WebKit, ainsi que des locators avec attente automatique qui réduisent les sélecteurs instables.
Playwright a été créé par certains des ingénieurs originaux de Puppeteer et partage la majeure partie de son API, la migration consiste donc généralement à renommer quelques appels. Ses locators attendent automatiquement que les éléments soient actionnables, ce qui supprime de nombreux appels manuels à waitForSelector. Le scraper équivalent ressemble à ceci :
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (compatible; MyScraper/1.0)'
});
const page = await context.newPage();
await page.goto('https://example.com/feed', { waitUntil: 'networkidle' });
await page.locator('.product').first().waitFor();
const items = await page.locator('.product').evaluateAll((els) =>
els.map((el) => ({
title: el.querySelector('h2')?.innerText.trim(),
price: el.querySelector('.price')?.innerText.trim()
}))
);
console.log(`Extracted ${items.length} items`);
await browser.close();
Choisissez Playwright lorsque vous devez tester le même scraper sur plusieurs moteurs de navigateur ou souhaitez profiter de sa simulation réseau intégrée. Choisissez Puppeteer lorsque vous ciblez uniquement Chrome et voulez la dépendance la plus légère. Les deux consomment bien plus de mémoire que fetch et cheerio, réservez-les aux pages qui nécessitent réellement un rendu.
Quel outil de scraping JavaScript choisir ?
Utilisez fetch ou axios avec cheerio pour du HTML statique, et n’ayez recours à Puppeteer ou Playwright que lorsqu’une page rend ses données dans le navigateur.
Le bon outil dépend de la façon dont la page livre les données et de l’équilibre recherché entre vitesse et fidélité de rendu. Les clients HTTP sont rapides et légers mais ne peuvent pas exécuter de JavaScript ; les navigateurs headless exécutent le même JavaScript que celui visible par un utilisateur réel, mais coûtent plus de mémoire et de temps. Le tableau ci-dessous résume les compromis :
| Outil | Exécute JS | Vitesse | Idéal pour |
|---|---|---|---|
| fetch + cheerio | Non | La plus rapide | HTML statique, volume élevé |
| axios + cheerio | Non | Rapide | HTML statique, configuration proxy simple |
| Puppeteer | Oui | Lente | Pages dynamiques Chrome uniquement |
| Playwright | Oui | Lente | Dynamique, tests multi-navigateurs |
Un modèle de production courant combine les deux : utilisez un navigateur headless uniquement pour atteindre les pages ou points de terminaison nécessitant un rendu, puis utilisez ensuite un simple client HTTP pour le gros des requêtes. JavaScript n’est qu’une pile parmi plusieurs viables pour ce travail ; si vous comparez des écosystèmes, notre guide sur le web scraping avec Rust couvre une alternative compilée.
Comment router des scrapers JavaScript via un proxy ?
Passez les paramètres du proxy à votre client HTTP ou aux options de lancement du navigateur afin que les requêtes proviennent une adresse IP différente, ce qui répartit la charge et réduit le risque qu’une seule adresse soit limitée en débit.
Scraper de nombreuses pages depuis une seule IP est le moyen le plus sûr d’être bloqué. Faire tourner les requêtes sur un pool d’adresses donne au trafic l’apparence à celui de nombreux visiteurs distincts. Avec axios, vous fournissez l’hôte, le port et les identifiants du proxy dans la configuration de la requête. L’exemple ci-dessous passe par une passerelle DataImpulse :
import axios from 'axios';
const { data } = await axios.get('https://example.com', {
proxy: {
protocol: 'http',
host: 'gw.dataimpulse.com',
port: 823,
auth: { username: 'YOUR_USERNAME', password: 'YOUR_PASSWORD' }
},
headers: { 'User-Agent': 'Mozilla/5.0 (compatible; MyScraper/1.0)' }
});
console.log(data.length, 'bytes received through the proxy');
Puppeteer reçoit le proxy en argument de lancement puis s’authentifie sur la page, car Chrome n’accepte pas les identifiants dans l’URL du proxy. Le principe est le même avec Playwright, qui accepte directement un objet proxy avec un nom d’utilisateur et un mot de passe :
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
headless: 'new',
args: ['--proxy-server=gw.dataimpulse.com:823']
});
const page = await browser.newPage();
await page.authenticate({
username: 'YOUR_USERNAME',
password: 'YOUR_PASSWORD'
});
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
console.log(await page.title());
await browser.close();
DataImpulse propose des proxies résidentiels, des proxies datacenter et des proxies mobiles via HTTP, HTTPS et SOCKS5, avec sessions rotatives et persistantes. Le ciblage par pays est inclus, tandis que le ciblage par État, ville, code postal et ASN est proposé en option payante. Si une requête proxy renvoie une erreur d’authentification, notre note sur l’erreur HTTP 407 explique la cause la plus courante, et notre guide sur l’authentification proxy détaille les différents formats d’identifiants.
Comment intercepter les XHR et les points de terminaison JSON ?
Au lieu de scraper le HTML rendu, surveillez le trafic réseau depuis un navigateur headless et lisez les réponses JSON que la page récupère en arrière-plan, ce qui est souvent plus propre et plus rapide.
Les pages dynamiques obtiennent généralement leurs données d’une API cachée qui renvoie du JSON. La lecture directe de cette réponse évite complètement l’analyse du DOM et vous donne des enregistrements bien structurés. Puppeteer et Playwright permettent d’écouter les réponses et de capturer celles qui vous intéressent. L’exemple Puppeteer ci-dessous capture toute réponse JSON provenant d’un chemin d’API :
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
const captured = [];
page.on('response', async (response) => {
const url = response.url();
if (url.includes('/api/') && response.headers()['content-type']?.includes('application/json')) {
try {
captured.push(await response.json());
} catch (err) {
// non-JSON body, ignore
}
}
});
await page.goto('https://example.com/feed', { waitUntil: 'networkidle2' });
console.log(`Captured ${captured.length} JSON payloads`);
await browser.close();
Dès que vous connaissez l’URL du point de terminaison et ses paramètres, vous pouvez souvent vous passer entièrement du navigateur et appeler l’API JSON avec un simple fetch, en transmettant les mêmes en-têtes que ceux envoyés par la page. Cela vous donne la vitesse d’un scraper statique avec l’exhaustivité d’un scraper utilisant le rendu, et c’est le plus grand gain de performance disponible pour les sites dynamiques.
Quelles sont les limites éthiques et légales du scraping ?
Ne scrapez que des données publiques, respectez le robots.txt et les conditions d’utilisation du site, évitez les données personnelles, et ne surchargez jamais un serveur. Ces pratiques rendent votre projet à la fois défendable et durable.
Consultez le fichier robots.txt à la racine du domaine pour connaître les chemins que le site demande aux robots d’éviter, et prenez ces demandes au sérieux. Identifiez votre scraper avec un User-Agent honnête, limitez vos requêtes, et mettez en cache les réponses pour ne pas récupérer inutilement la mêmes pages. Côté infrastructure, l’origine de vos adresses IP compte. DataImpulse exploite des proxies éthiques fournis par des utilisateurs consentants et rémunérés, dans le respect du RGPD, avec un accord de traitement des données disponible. L’éthique n’est pas seulement une question de conformité ; les scrapers polis et transparents sont aussi ceux qui continuent de fonctionner. Pour les principes d’une collecte durable, consultez notre guide sur le web scraping éthique.

Questions fréquentes
Ai-je besoin de Node.js pour scraper avec JavaScript ?
Pour tout ce qui dépasse un test rapide d’une seule page, oui. Node.js vous permet d’exécuter des scrapers sur un serveur, de parcourir de nombreuses URL, de gérer les nouvelles tentatives, d’exporter des données et d’utiliser des proxies, autant de tâches que la console du navigateur gère mal.
Quand dois-je utiliser Puppeteer plutôt que fetch et cheerio ?
Utilisez fetch ou axios avec cheerio lorsque les données sont présentes dans la réponse HTML initiale. Passez à Puppeteer ou Playwright uniquement lorsque le contenu est rendu par JavaScript après le chargement de la page, car un navigateur headless est bien plus lourd.
Comment exporter les données scrapées en CSV dans Node.js ?
Collectez les enregistrements sous forme de tableau d’objets, puis construisez des lignes séparées par des virgules en joignant les valeurs de chaque objet et en échappant les guillemets et les virgules. Écrivez le résultat dans un fichier avec le module fs intégré, ou utilisez une bibliothèque comme csv-stringify.
Pourquoi suis-je bloqué en scrapant avec JavaScript ?
Les causes courantes sont trop de requêtes depuis une seule IP, un User-Agent manquant ou suspect, et aucun délai entre les requêtes. Les proxies rotatifs, des en-têtes réalistes, des nouvelles tentatives avec backoff et la limitation de débit réduisent les blocages.
Quel type de proxy est le meilleur pour le scraping JavaScript ?
Les proxies datacenter sont rapides et bon marché pour des cibles tolérantes, tandis que les proxies résidentiels et mobiles ressemblent davantage à de vrais utilisateurs sur des sites stricts. Les sessions rotatives permettent à répartir les requêtes sur de nombreuses adresses.
Quand DataImpulse n’est-il pas le bon choix ?
Si vous avez besoin de proxies ISP statiques, d’une API de scraping entièrement gérée, ou d’un accès à des sites bancaires et gouvernementaux, DataImpulse ne sera pas le bon choix. Il se concentre sur les proxies résidentiels, mobiles et datacenter rotatifs pour la collecte de données publiques et l’accès à des contenus.
Commencez à scraper avec des proxies fiables
Prêt à garder vos scrapers JavaScript opérationnels sans blocages répétés ? DataImpulse vous donne des IP résidentielles, mobiles et datacenter issues de sources éthiques à partir de 1 dollar par Go avec un trafic sans expiration et sans abonnement. Créez un compte et routez votre premier scraper via un proxy dès aujourd’hui.
