Table of Contents
Introduction
L’exploration automatique de sites web (web crawling) est une méthode puissante pour extraire et structurer des données de manière automatisée. Grâce à Puppeteer, un framework basé sur Chromium, il est possible de naviguer sur des sites dynamiques, d’interagir avec des éléments (comme des boutons et des liens) et de télécharger des fichiers.
Dans cet article, nous verrons comment créer un crawler intelligent qui :
- Explore un site web en suivant les liens internes et externes.
- Télécharge des documents (PDF, Word, Excel, etc.).
- Organise les fichiers en respectant l’arborescence du site.
- Gère les erreurs et optimise les performances.
Pourquoi utiliser Puppeteer pour le Web Scraping ?
Contrairement aux requêtes HTTP classiques avec axios
ou fetch
, Puppeteer simule un véritable navigateur et permet :
✅ D’exécuter du JavaScript (essentiel pour les sites SPA – Single Page Application).
✅ De cliquer sur des boutons dynamiques (fa-eye
, fa-external-link-alt
).
✅ De contourner certaines protections contre les crawlers (ex. chargement différé, AJAX, authentification).
✅ D’automatiser le téléchargement de fichiers.
Définition des objectifs
Nous allons créer un crawler en Node.js capable de :
- Explorer le site de manière récursive, en respectant une profondeur maximale.
- Cliquer sur des boutons dynamiques (
fa-eye
,fa-external-link-alt
,routerLink
). - Télécharger et organiser les fichiers dans un répertoire local.
- Gérer les erreurs et optimiser le temps d’exécution.
Mettre à jour Node.js sous Ubuntu
Sous Ubuntu, vous pouvez mettre à jour Node.js en utilisant NVM (recommandé) ou en installant la dernière version depuis les dépôts officiels.
1. Vérifier votre version actuelle de Node.js
Avant de mettre à jour, vérifiez votre version actuelle en exécutant :
node -v
Si la version affichée est inférieure à 16.x, vous devez la mettre à jour.
2. Mettre à jour Node.js avec NVM (recommandé)
Si vous utilisez NVM (Node Version Manager), c’est la méthode la plus simple et flexible :
- Installer NVM (si ce n’est pas encore fait)
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.4/install.sh | bash source ~/.bashrc
- Installer la dernière version stable de Node.js
nvm install 18 nvm use 18
- Vérifier que la nouvelle version est bien utilisée
node -v
- Définir cette version comme la version par défaut
nvm alias default 18
3. Mettre à jour Node.js via les dépôts officiels
Si vous préférez installer Node.js directement depuis les dépôts, voici la procédure :
- Ajouter le dépôt officiel de Node.js (pour la version 18)
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
- Installer Node.js et npm
sudo apt install -y nodejs
- Vérifier que Node.js et npm sont bien installés
node -v npm -v
4. Nettoyer l’ancienne version et forcer la mise à jour
Si vous avez déjà une version obsolète installée, vous pouvez la supprimer avant d’installer la nouvelle :
sudo apt remove -y nodejs npm
sudo apt autoremove -y
Puis relancez l’installation avec la méthode de votre choix.
5. Exécuter votre script après la mise à jour
Une fois Node.js mis à jour en version 18+, essayez de relancer votre script :
node aspire.mjs
Si tout fonctionne bien, le problème de timers/promises
sera résolu ! 🚀
Installation de Puppeteer
Tout d’abord, il faut installer Puppeteer dans un projet Node.js.
npm init -y
npm install puppeteer website-scraper website-scraper-puppeteer website-scraper-existing-directory
Si vous souhaitez éviter le téléchargement automatique de Chromium (gain de place), utilisez :
npm install puppeteer-core
Dans ce cas, il faudra spécifier le chemin d’un navigateur Chromium installé.
Voici un aperçu des packages installés :
- puppeteer : Permet d’automatiser l’accès au site Web, y compris la gestion de l’authentification et l’extraction des liens.
- website-scraper : Gère l’aspiration et l’enregistrement des pages Web sur votre disque.
- website-scraper-puppeteer : Plugin qui permet à website-scraper d’utiliser Puppeteer pour interagir avec des sites dynamiques.
- website-scraper-existing-directory : Plugin qui permet de stocker les fichiers téléchargés dans un dossier existant sans conflit.
2. Mise en place du Web Crawler
Voici le code de base de notre crawler Puppeteer :
2.1 Configuration du projet
On définit :
- L’URL du site cible (anonymisée pour respecter la confidentialité).
- Le répertoire de stockage des fichiers.
- Le nombre maximal de niveaux d’exploration.
import puppeteer from 'puppeteer';
import fs from 'fs';
import path from 'path';
const username = 'user'; // Modifier si nécessaire
const password = 'password'; // Modifier si nécessaire
const baseURL = 'https://example.com/'; // URL à crawler
const saveDirectory = './site-aspire';
const maxDepth = 5; // Profondeur d'exploration maximale
if (!fs.existsSync(saveDirectory)) {
fs.mkdirSync(saveDirectory, { recursive: true });
}
function normalizeUrl(url) {
try {
let u = new URL(url);
u.hash = '';
u.search = '';
return u.href;
} catch (e) {
return null;
}
}
2.2 Organisation du stockage
Chaque page visitée et chaque fichier téléchargé seront enregistrés dans un répertoire structuré.
Exemple d’organisation :
site-aspire/
└── example.com/
├── doc-fonc/
│ ├── gfc/
│ │ ├── lien1-index.html
│ │ ├── lien1-document1.pdf
│ │ ├── lien1-document2.docx
│ │ ├── lien2-index.html
│ │ └── lien2-document1.xlsx
📌 Convention des noms de fichiers :
lienX-index.html
→ Capture des pages HTML accessibles viafa-external-link-alt
.lienX-documentX.pdf
→ Documents PDF téléchargés viafa-eye
.
2.3 Gestion de l’authentification et de l’interception des téléchargements
Certains documents ne peuvent être récupérés qu’après authentification. Pour cela, nous utilisons Basic Auth.
async function downloadDirectWithAuth(url) {
console.log(`Téléchargement direct : ${url}`);
const authHeader = 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64');
try {
const res = await fetch(url, {
headers: { 'Authorization': authHeader, 'User-Agent': 'Mozilla/5.0' }
});
if (!res.ok) {
console.log(`Echec : ${res.status} ${res.statusText}`);
return;
}
const buffer = Buffer.from(await res.arrayBuffer());
if (buffer.length > 0) {
const urlObj = new URL(url);
let filePath = path.join(saveDirectory, urlObj.hostname, urlObj.pathname);
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, buffer);
console.log(`Document téléchargé : ${url} -> ${filePath}`);
}
} catch (e) {
console.log(`Erreur téléchargement direct: ${e}`);
}
}
3. Exploration récursive du site
Notre fonction explorePageRecursively
:
- Clique sur les éléments interactifs (
fa-eye
,fa-external-link-alt
,routerLink
). - Télécharge les documents et sauvegarde les pages HTML.
- Suit une structure de navigation récursive pour éviter les doublons.
async function explorePageRecursively(page, visitedUrls = new Set(), depth = 0) {
if (depth > maxDepth) return;
const currentUrl = page.url();
if (visitedUrls.has(currentUrl)) return;
visitedUrls.add(currentUrl);
console.log(`Exploration : ${currentUrl} (profondeur : ${depth})`);
saveHtml(currentUrl, await page.content());
async function clickAndExplore(selector, label) {
const elements = await page.$$(selector);
for (let i = 0; i < elements.length; i++) {
try {
console.log(`Clic sur ${label} ${i + 1}/${elements.length}`);
await elements[i].click();
await page.waitForTimeout(3000);
if (label === 'fa-external-link-alt') saveHtml(page.url(), await page.content());
await explorePageRecursively(page, visitedUrls, depth + 1);
await page.goto(currentUrl, { waitUntil: 'networkidle2' });
} catch (e) {
console.error(`Erreur clic sur ${label}:`, e);
}
}
}
await clickAndExplore('i.fa-eye', 'fa-eye');
await clickAndExplore('i.fa-external-link-alt', 'fa-external-link-alt');
const routerLinks = await page.$$eval('[routerlink]', elems => elems.map(el => el.getAttribute('routerlink')));
for (const routerLink of routerLinks) {
const fullUrl = new URL(routerLink, currentUrl).href;
if (!visitedUrls.has(fullUrl)) {
await page.goto(fullUrl, { waitUntil: 'networkidle2' });
await explorePageRecursively(page, visitedUrls, depth + 1);
await page.goto(currentUrl, { waitUntil: 'networkidle2' });
}
}
}
4. Lancement du crawler
Enfin, nous démarrons le crawler avec Puppeteer.
(async () => {
const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
await page.authenticate({ username, password });
console.log(`Démarrage du crawl à partir de : ${baseURL}`);
await page.goto(baseURL, { waitUntil: 'networkidle2' });
await explorePageRecursively(page);
await browser.close();
console.log('Crawl terminé avec succès !');
})();
Exécution du Scraper
Une fois le fichier créé, vous pouvez exécuter le script avec la commande suivante :
node scraper.mjs
Pendant l’exécution :
- Le script récupère automatiquement tous les liens Angular dynamiques.
- Il scrape les pages découvertes et télécharge leur contenu.
- Il stocke tous les fichiers dans le dossier
site-aspire/
de manière organisée.
Conclusion
Ce crawler Puppeteer est capable : ✅ D’explorer un site dynamiquement via ses liens et boutons.
✅ De télécharger les documents automatiquement.
✅ De sauvegarder et structurer les pages HTML.
✅ De gérer l’authentification et éviter les doublons.
🔎 Optimisations possibles :
- Gérer les captcha / protections anti-bot.
- Ajouter un mécanisme de parallélisation pour optimiser le temps d’exécution.
- Ajouter une interface web pour monitorer les explorations.
💡 À vous de tester et d’améliorer ce crawler selon vos besoins ! 🚀