Web scraping d'un site Prestashop avec Puppeteer

Article écrit le

Scraping Prestashop grâce à Puppeteer : le code complet

En septembre 2024, j'ai reçu une demande d'un client pour scrapper les produits de plusieurs sites e-commerce basés sur Prestashop. Cette pratique, appelée web scraping, consiste à extraire des informations précises d’un site web pour les réutiliser, souvent à des fins de gestion de catalogue ou de suivi de produits. Voici un aperçu de la démarche, des attentes du client, et des outils utilisés pour répondre à ce type de demande.

La Demande Initiale

Le client souhaitait récupérer des données produits de trois sites partenaires. Ce genre de projet n'est pas nouveau, mais le client avait déjà rencontré des problèmes avec d'autres développeurs, ce qui l’a conduit à être plus prudent. Il voulait donc un exemple de fichier de test pour s'assurer de la qualité du scraping.

Les Outils : Puppeteer

Pour réaliser ce type de projet, j'utilise Puppeteer, une bibliothèque Node.js qui permet de contrôler un navigateur sans interface (headless browser). Elle est particulièrement utile pour scrapper des sites dynamiques comme ceux utilisant JavaScript, typiquement rencontrés sur les plateformes e-commerce modernes comme Prestashop. Puppeteer est fiable pour extraire des données même lorsque celles-ci ne sont pas directement accessibles dans le code source HTML, mais générées par JavaScript.

Déroulement du Projet

Identification des Sites à Scrapper

Une fois les sites cibles identifiés, le processus de scraping peut commencer. Le client m'a fourni les URL des sites concernés, sur lesquels j’ai pu repérer les pages produits et structurer le processus d’extraction de données. Cela implique d’analyser les éléments HTML pour identifier les balises où les informations (prix, titre, description, images) sont stockées.

Test et Validation

Le client a demandé un fichier test au format XLSX contenant des informations produits, ce qui est standard pour vérifier l'exactitude du scraping. Un fichier test permet au client de s'assurer que toutes les informations clés (comme les titres, descriptions, prix, et images) sont bien extraites et formatées.

Adaptation aux Différents Sites

Chaque site Prestashop peut présenter une structure légèrement différente, ce qui signifie qu’il faut parfois adapter le script pour extraire correctement les informations. Puppeteer permet de faire cela efficacement, notamment en gérant les cookies, les redirections, et en simulant des actions d'utilisateur (comme le défilement ou les clics).

Code d'exemple avec Puppeteer et MySQL

Le code suivant permet de scraper les produits d'un site Prestashop et d'insérer les données extraites dans une base de données MySQL :


import puppeteer from 'puppeteer';
import mysql from 'mysql2/promise'; // Pour mysql2

// Configuration de la connexion à la base de données
const dbConfig = {
    host: 'localhost',
    user: 'user_bdd',
    password: 'password_bdd',
    database: 'name_bdd'
};

const selectors = {
    baseUrl: 'https://www.site-prestashop-cible.fr/index.php?controller=product&id_product=',
    startId: 99, //1
    endId: 1000, //1000
    breadcrumb: '.breadcrumb ol li',
    productId: 'input[name="id_product"]',
    ogTitle: 'meta[property="og:title"]',
    metaDescription: 'meta[name="description"]',
    productName: 'h1',
    description: 'section.product-description-section',
    shortDescription: '[id^="product-description-short-"]',
    images: '#product-images-thumbs .slick-slide img',
    price: 'span[itemprop="price"]',
    features: 'section.product-features .data-sheet',
};

// Fonction pour générer les URLs des produits
const generateProductUrls = (startId, endId) => {
    const urls = [];
    for (let id = startId; id <= endId; id++) {
        urls.push(`${selectors.baseUrl}${id}`);
    }
    return urls;
};

// Fonction de délai aléatoire
const randomDelay = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

// Fonction pour nettoyer les descriptions
const scrapeDescription = async (page) => {
    const descriptionElement = await page.$(selectors.description);
    if (descriptionElement) {
        const description = await page.evaluate((element) => {
            const clone = element.cloneNode(true);
            clone.querySelectorAll('style').forEach(el => el.remove());
            clone.querySelectorAll('i:empty, span:empty').forEach(el => el.remove());
            clone.querySelectorAll('div').forEach(el => {
                const parent = el.parentNode;
                while (el.firstChild) parent.insertBefore(el.firstChild, el);
                parent.removeChild(el);
            });
            clone.querySelectorAll('a').forEach(el => {
                const parent = el.parentNode;
                while (el.firstChild) parent.insertBefore(el.firstChild, el);
                parent.removeChild(el);
            });
            clone.querySelectorAll('*').forEach(el => el.removeAttribute('class'));
            return clone.innerHTML;
        }, descriptionElement);

        return description.trim().replace(/\s+/g, ' ').replace(/\s*<\/span>/g, '');
    }
    return '';
};

// Fonction pour insérer les données dans la base de données
const insertProductData = async (connection, productInfo) => {
    const safeProductInfo = {
        productId: productInfo.productId || null,
        breadcrumb: productInfo.breadcrumb || null,
        name: productInfo.name || null,
        description: productInfo.description || null,
        shortDescription: productInfo.shortDescription || null,
        price: productInfo.price || null,
        features: productInfo.features || null,
        images: productInfo.images || null,
        ogTitle: productInfo.ogTitle || null,
        metaDescription: productInfo.metaDescription || null,
        ogUrl: productInfo.ogUrl || null
    };

    const query = `
        INSERT INTO toorxfitness (id_product, ogUrl, breadcrumb, name, description, shortDescription, price, features, images, ogTitle, metaDescription)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ON DUPLICATE KEY UPDATE
            ogUrl = VALUES(ogUrl),
            breadcrumb = VALUES(breadcrumb),
            name = VALUES(name),
            description = VALUES(description),
            shortDescription = VALUES(shortDescription),
            price = VALUES(price),
            features = VALUES(features),
            images = VALUES(images),
            ogTitle = VALUES(ogTitle),
            metaDescription = VALUES(metaDescription)
    `;
    const values = [
        safeProductInfo.productId,
        safeProductInfo.ogUrl,
        safeProductInfo.breadcrumb,
        safeProductInfo.name,
        safeProductInfo.description,
        safeProductInfo.shortDescription,
        safeProductInfo.price,
        safeProductInfo.features,
        safeProductInfo.images,
        safeProductInfo.ogTitle,
        safeProductInfo.metaDescription
    ];
    await connection.execute(query, values);
};

// Fonction principale de scraping
const scrapeProduct = async (browser, url, connection, idsSeen = new Set()) => {
    const page = await browser.newPage();
    await page.setViewport({ width: 1080, height: 1024 });

    // Logique de scraping
};

// Fonction pour démarrer le scraping
const startScraping = async () => {
    const browser = await puppeteer.launch({
        headless: true,
        args: ['--no-sandbox', '--disable-setuid-sandbox'],
        ignoreHTTPSErrors: true
    });

    const connection = await mysql.createConnection(dbConfig);
    await connection.connect();

    const productsToScrape = generateProductUrls(selectors.startId, selectors.endId);
    const idsSeen = new Set(); // Set pour éviter les doublons d'ID

    for (const url of productsToScrape) {
        await scrapeProduct(browser, url, connection, idsSeen);
    }

    await connection.end();
    await browser.close();
};

startScraping();

Analyse des Fonctionnalités du Code

Ce script utilise Puppeteer pour automatiser le scraping des produits sur un site Prestashop. Puppeteer, une bibliothèque Node.js, permet de contrôler un navigateur sans interface graphique. Le script interagit avec le DOM pour extraire les informations relatives aux produits et utilise MySQL pour stocker les données.

1. Connexion à la Base de Données

Le script commence par configurer la connexion à une base de données MySQL à l'aide de mysql2/promise, une bibliothèque qui permet d'exécuter des requêtes SQL de manière asynchrone. Cela garantit que les données produits extraites sont stockées efficacement dans une base de données relationnelle.

2. Sélecteurs pour l’Extraction de Données

Les éléments HTML à scraper, comme les titres de produits, prix, images, et descriptions, sont définis via un objet selectors. Ces sélecteurs correspondent aux balises spécifiques où ces informations sont stockées sur les pages produits du site Prestashop.

3. Génération des URLs des Produits

Le script génère une liste d'URLs de produits à scraper en fonction de l’ID de départ et de l’ID de fin définis dans le code. Cette approche permet d’automatiser la navigation sur plusieurs pages produits sans intervention manuelle.

4. Gestion du Délai Aléatoire

Une fonction de délai aléatoire est implémentée pour simuler un comportement plus humain lors de la navigation d'une page à l'autre. Cela réduit le risque d'être détecté par les systèmes anti-bots des sites Prestashop.

5. Nettoyage des Descriptions

La fonction scrapeDescription est utilisée pour nettoyer les descriptions extraites en supprimant les balises inutiles comme les balises de style, les balises vides et les classes. Cela garantit que seules les informations pertinentes sont sauvegardées dans la base de données.

6. Insertion des Données dans MySQL

Une fois les informations extraites, elles sont insérées dans une base de données MySQL via la fonction insertProductData. Cette fonction utilise des requêtes SQL sécurisées pour insérer ou mettre à jour les données produits afin d'éviter les doublons.

7. Lancement du Scraping

La fonction startScraping lance le processus de scraping en utilisant Puppeteer pour ouvrir un navigateur sans interface et en parcourant les URLs des produits générés. Chaque page produit est scrutée, et les données sont extraites, puis stockées dans la base de données.

8. Installation et Utilisation de PM2

Pour exécuter et gérer ce script de scraping de manière efficace en arrière-plan, vous pouvez utiliser PM2, un gestionnaire de processus pour Node.js. PM2 permet de garder vos scripts actifs, même en cas de crash, et de les redémarrer automatiquement si nécessaire.

Voici les étapes pour installer et utiliser PM2 :

  • Installez PM2 globalement sur votre système avec la commande suivante :
  • npm install -g pm2
  • Ensuite, démarrez le script avec la commande pm2 start. Vous pouvez nommer le processus pour un suivi plus facile :
  • pm2 start scrapNomDuSite.js --name=scrapNomDuSite
  • Cela permet d'exécuter le script scrapNomDuSite.js en arrière-plan sous le nom personnalisé scrapNomDuSite. Vous pouvez surveiller et gérer vos processus avec pm2 list, et redémarrer ou arrêter les processus avec les commandes pm2 restart et pm2 stop.

PM2 est donc un outil puissant pour garantir que vos scripts de scraping continuent de fonctionner sans interruption.

Conclusion

Le web scraping est une solution idéale pour automatiser la récupération d’informations sur des sites e-commerce, surtout lorsque le volume de produits est élevé. Avec un outil comme Puppeteer, il est possible d’automatiser ce processus tout en garantissant la précision des données extraites. Pour toute personne intéressée par l’automatisation de la gestion de catalogue, le web scraping avec Puppeteer sur des plateformes comme Prestashop est une méthode efficace et éprouvée.

Alexandre Carette

Expert Prestashop & Shopify, éducateur & CEO CodemyShop

Discussion (20)

Very straight-to-point article. Really worth time reading. Thank you! But tools are just the instruments for the UX designers. The knowledge of the design tools are as important as the creation of the design strategy.

Much appreciated! Glad you liked it ☺️

The article covers the essentials, challenges, myths and stages the UX designer should consider while creating the design strategy.

Thanks for sharing this. I do came from the Backend development and explored some of the tools to design my Side Projects.

Sign up for our newsletter

Stay up to date with the roadmap progress, announcements and exclusive discounts feel free to sign up with your email.