🔌 API & WebserviceAvancé PS 1.6 PS 1.7 PS 8.x

Extraire les données produits PrestaShop avec Scrapy (Python)

Guide complet pour extraire et synchroniser les données produits PrestaShop avec Scrapy en Python. Scraping, export CSV, synchronisation de stocks.

En bref : Guide complet pour extraire les données produits d'une boutique PrestaShop avec le framework Python Scrapy : export CSV du catalogue et synchronisation de stocks entre deux sites, avec comparatif API Webservice vs scraping.

Publié le 21 mars 2026 8 min de lecture Alexandre Carette

Pourquoi extraire des données depuis un PrestaShop ?

Dans le quotidien d'un développeur e-commerce, certaines situations exigent de récupérer massivement des données depuis une boutique PrestaShop : migration vers une nouvelle instance, synchronisation de stocks entre deux sites, audit SEO des fiches produits, ou encore constitution d'un catalogue CSV pour un marketplace tiers.

Si l'API Webservice de PrestaShop reste la voie royale pour accéder aux données de manière structurée, il arrive que celle-ci soit désactivée, mal configurée, ou que l'on doive travailler sur un site dont on n'a pas l'accès back-office. Dans ces cas précis, le web scraping avec un framework Python comme Scrapy offre une alternative robuste.

Important : Le scraping doit toujours être réalisé sur des sites dont vous êtes propriétaire ou pour lesquels vous disposez d'une autorisation explicite. Respectez le fichier robots.txt et les conditions d'utilisation du site cible.

Prérequis et installation de Scrapy

Environnement recommandé

  • **Python 3.10+** (Python 2 n'est plus supporté)
  • **Scrapy 2.11+** (version stable actuelle)
  • Un environnement virtuel pour isoler les dépendances

Installation


# Créer un environnement virtuel dédié
python3 -m venv ~/scrapy-env
source ~/scrapy-env/bin/activate

# Installer Scrapy
pip install Scrapy

# Vérifier l'installation
scrapy version

Sur Ubuntu/Debian, certaines dépendances système peuvent être nécessaires :


sudo apt install python3-dev libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev

Cas n°1 : Extraire le catalogue produits en CSV

Créer le projet Scrapy


mkdir -p ~/mes_crawlers && cd ~/mes_crawlers
scrapy startproject prestashop_scraper
cd prestashop_scraper

Scrapy génère cette arborescence :


prestashop_scraper/
├── scrapy.cfg
└── prestashop_scraper/
    ├── __init__.py
    ├── items.py
    ├── middlewares.py
    ├── pipelines.py
    ├── settings.py
    └── spiders/
        └── __init__.py

Définir le modèle de données (`items.py`)

On déclare les champs que l'on souhaite extraire de chaque fiche produit :


# -*- coding: utf-8 -*-
from scrapy.item import Item, Field


class PrestashopProductItem(Item):
    """Modèle de données pour un produit PrestaShop."""
    url = Field()
    id_product = Field()
    name = Field()
    reference = Field()
    price = Field()
    quantity = Field()
    balise_title = Field()
    meta_description = Field()
    description_courte = Field()
    description = Field()
    category = Field()
    image_url = Field()
    ean13 = Field()

Créer le spider (`spiders/products_spider.py`)

Le spider est le composant qui navigue sur le site et extrait les données. Voici un exemple adapté au thème classic de PrestaShop :


# -*- coding: utf-8 -*-
import scrapy
from prestashop_scraper.items import PrestashopProductItem


class ProductsSpider(scrapy.Spider):
    name = 'products'
    allowed_domains = ['votre-boutique.com']
    start_urls = ['https://votre-boutique.com/2-accueil']

    custom_settings = {
        'DOWNLOAD_DELAY': 1,  # 1 seconde entre chaque requête
        'CONCURRENT_REQUESTS': 1,  # Une requête à la fois
        'ROBOTSTXT_OBEY': True,
        'USER_AGENT': 'CatalogBot/1.0 (contact@votre-domaine.com)',
    }

    def parse(self, response):
        """Parse les pages de listing et suit la pagination."""
        # Liens vers les fiches produits
        product_links = response.css('a.product-thumbnail::attr(href)').getall()
        for link in product_links:
            yield scrapy.Request(link, callback=self.parse_product)

        # Pagination : suivre la page suivante
        next_page = response.css('a.next::attr(href)').get()
        if next_page:
            yield scrapy.Request(next_page, callback=self.parse)

        # Sous-catégories
        subcategories = response.css('#subcategories a::attr(href)').getall()
        for subcat in subcategories:
            yield scrapy.Request(subcat, callback=self.parse)

    def parse_product(self, response):
        """Extrait les données d'une fiche produit."""
        item = PrestashopProductItem()

        item['url'] = response.url
        item['balise_title'] = response.css('title::text').get('').strip()
        item['meta_description'] = response.xpath(
            '//meta[@name="description"]/@content'
        ).get('').strip()
        item['name'] = response.css('h1.product-detail-name::text').get('').strip()

        # Prix : nettoyage du format
        raw_price = response.css('span.current-price-value::attr(content)').get()
        item['price'] = raw_price if raw_price else '0'

        # Référence produit
        item['reference'] = response.css(
            'span.product-reference-value::text'
        ).get('').strip()

        # Description
        item['description_courte'] = response.css(
            'div.product-description-short'
        ).get('').strip()

        # Image principale
        item['image_url'] = response.css(
            'img.js-qv-product-cover::attr(src)'
        ).get('')

        # ID produit depuis l'URL ou le DOM
        id_input = response.css('input[name="id_product"]::attr(value)').get()
        item['id_product'] = id_input if id_input else ''

        yield item

Adapter les sélecteurs selon la version PrestaShop

Les sélecteurs CSS varient considérablement d'une version à l'autre :

ÉlémentPrestaShop 1.6 (default)PrestaShop 1.7/8.x (classic) Lien produit listing`a.product_img_link``a.product-thumbnail` Nom produit`h1[itemprop="name"]``h1.product-detail-name` Prix`span#our_price_display``span.current-price-value` Pagination suivante`li.pagination_next a``a.next` Image produit`img#bigpic``img.js-qv-product-cover`

Conseil : Avant de coder votre spider, inspectez le DOM du site cible avec les outils développeur de votre navigateur. Les thèmes personnalisés modifient souvent ces sélecteurs.

Configurer l'export CSV (`settings.py`)


# Activer l'export CSV automatique
FEEDS = {
    'produits_%(time)s.csv': {
        'format': 'csv',
        'encoding': 'utf-8',
        'fields': [
            'id_product', 'name', 'reference', 'price',
            'balise_title', 'meta_description', 'url', 'image_url'
        ],
        'overwrite': True,
    },
}

# Respecter le robots.txt
ROBOTSTXT_OBEY = True

# Délai entre les requêtes (en secondes)
DOWNLOAD_DELAY = 1

# Désactiver les cookies pour éviter les sessions inutiles
COOKIES_ENABLED = False

# Logging
LOG_LEVEL = 'INFO'

Lancer le crawl


scrapy crawl products

Le fichier CSV sera généré à la racine du projet avec un horodatage.

Cas n°2 : Synchroniser les stocks entre deux boutiques

Un besoin fréquent consiste à maintenir les stocks synchronisés entre un site source (fournisseur) et un site destination (revendeur). Voici une approche complète.

Modèle de données pour les stocks (`items.py`)


class PrestashopStockItem(Item):
    """Modèle simplifié pour la synchronisation de stocks."""
    id_product = Field()
    quantity = Field()

Spider de récupération des stocks

Si le site source expose les quantités (ce qui est souvent le cas sur les thèmes par défaut via les attributs data-* ou les microdonnées), on peut les extraire :


class StockSpider(scrapy.Spider):
    name = 'stock_sync'
    allowed_domains = ['site-source.com']
    start_urls = ['https://site-source.com/sitemap.xml']

    custom_settings = {
        'DOWNLOAD_DELAY': 2,
        'ITEM_PIPELINES': {
            'prestashop_scraper.pipelines.StockSyncPipeline': 300,
        },
    }

    def parse(self, response):
        """Parse le sitemap pour trouver toutes les URLs produits."""
        response.selector.remove_namespaces()
        urls = response.xpath('//loc/text()').getall()
        for url in urls:
            if '/produit/' in url or '/product/' in url:
                yield scrapy.Request(url, callback=self.parse_stock)

    def parse_stock(self, response):
        """Extrait l'ID produit et la quantité disponible."""
        item = PrestashopStockItem()

        item['id_product'] = response.css(
            'input[name="id_product"]::attr(value)'
        ).get()

        # La quantité est souvent dans un attribut data-stock
        quantity = response.css(
            'span.product-availability::attr(data-stock)'
        ).get()

        if not quantity:
            # Fallback : chercher dans le JSON-LD
            import json
            ld_json = response.xpath(
                '//script[@type="application/ld+json"]/text()'
            ).getall()
            for block in ld_json:
                try:
                    data = json.loads(block)
                    if data.get('@type') == 'Product':
                        offers = data.get('offers', {})
                        availability = offers.get('availability', '')
                        quantity = '1' if 'InStock' in availability else '0'
                except json.JSONDecodeError:
                    continue

        item['quantity'] = int(quantity) if quantity else 0
        yield item

Pipeline de mise à jour via l'API Webservice (`pipelines.py`)

Plutôt que de manipuler la base de données directement, la bonne pratique consiste à utiliser l'API Webservice PrestaShop du site destination :


import requests
import xml.etree.ElementTree as ET


class StockSyncPipeline:
    """Met à jour les stocks sur le site destination via l'API Webservice."""

    def __init__(self):
        self.api_url = 'https://site-destination.com/api'
        self.api_key = 'VOTRE_CLE_WEBSERVICE'

    def process_item(self, item, spider):
        if not item.get('id_product'):
            return item

        id_product = item['id_product']
        quantity = item['quantity']

        try:
            # Récupérer le stock_available correspondant
            url = f"{self.api_url}/stock_availables"
            params = {
                'filter[id_product]': id_product,
                'filter[id_product_attribute]': 0,
                'display': 'full',
            }
            resp = requests.get(
                url, params=params,
                auth=(self.api_key, ''),
                timeout=10
            )

            if resp.status_code == 200:
                root = ET.fromstring(resp.content)
                stock_id = root.find('.//stock_available/id').text

                # Mettre à jour la quantité
                update_url = f"{self.api_url}/stock_availables/{stock_id}"
                xml_body = f"""<?xml version="1.0" encoding="UTF-8"?>
                <prestashop>
                    <stock_available>
                        <id>{stock_id}</id>
                        <quantity>{quantity}</quantity>
                    </stock_available>
                </prestashop>"""

                requests.put(
                    update_url,
                    data=xml_body,
                    auth=(self.api_key, ''),
                    headers={'Content-Type': 'application/xml'},
                    timeout=10
                )
                spider.logger.info(
                    f"Stock mis à jour : produit {id_product} → {quantity}"
                )

        except requests.RequestException as e:
            spider.logger.error(
                f"Erreur sync produit {id_product}: {e}"
            )

        return item

Automatiser avec un cron


# Synchronisation toutes les 6 heures
0 */6 * * * cd ~/mes_crawlers/prestashop_scraper && /home/user/scrapy-env/bin/scrapy crawl stock_sync >> /var/log/stock_sync.log 2>&1

La meilleure alternative : l'API Webservice PrestaShop

Avant de recourir au scraping, vérifiez si l'API Webservice n'est pas une meilleure solution pour votre cas d'usage. PrestaShop embarque une API REST native bien plus fiable :


import requests

API_URL = 'https://votre-boutique.com/api'
API_KEY = 'VOTRE_CLE_WEBSERVICE'

# Récupérer tous les produits en JSON
response = requests.get(
    f'{API_URL}/products',
    auth=(API_KEY, ''),
    params={'output_format': 'JSON', 'display': 'full'},
    timeout=30
)

products = response.json().get('products', [])
for product in products:
    print(f"ID: {product['id']} | Nom: {product['name'][0]['value']}")

Avantages de l'API vs scraping :

  • Données structurées et fiables (pas de parsing HTML fragile)
  • Accès aux déclinaisons, combinaisons, attributs spécifiques
  • Opérations CRUD complètes (lecture ET écriture)
  • Indépendant du thème front-office
  • Performances supérieures (pas de rendu HTML)

L'API nécessite un accès back-office pour générer la clé, mais c'est toujours l'approche à privilégier quand elle est disponible.

Bonnes pratiques et pièges à éviter

Respecter le site cible

  • **`DOWNLOAD_DELAY` :** Toujours mettre au minimum 1 seconde entre chaque requête pour ne pas surcharger le serveur
  • **`ROBOTSTXT_OBEY` :** Toujours à `True` sauf sur vos propres sites de test
  • **User-Agent :** Déclarez un User-Agent identifiable avec un email de contact

Gérer les pièges techniques

  • **Sessions PHP :** Désactivez les cookies (`COOKIES_ENABLED = False`) pour éviter de créer des milliers de sessions
  • **Pagination infinie :** Limitez la profondeur avec `DEPTH_LIMIT = 5`
  • **Doublons :** Scrapy déduplique automatiquement les URLs, mais vérifiez les paramètres GET inutiles
  • **Encodage :** PrestaShop 1.6 peut servir du ISO-8859-1 sur certaines configurations ; forcez l'UTF-8 dans vos settings

Thèmes personnalisés

Les sélecteurs CSS fournis dans cet article correspondent aux thèmes par défaut. Un thème personnalisé nécessitera d'adapter chaque sélecteur. Utilisez le shell interactif Scrapy pour tester vos sélecteurs avant de lancer un crawl complet :


scrapy shell 'https://votre-boutique.com/un-produit'
>>> response.css('h1::text').get()
>>> response.css('span.price::text').get()

Conclusion

Le web scraping avec Scrapy reste un outil puissant dans la boîte à outils du développeur PrestaShop, notamment pour les migrations, les audits SEO ou la veille concurrentielle. Cependant, pour la synchronisation de données entre deux boutiques que vous administrez, l'API Webservice sera toujours plus fiable, plus rapide et plus maintenable. Réservez le scraping aux situations où l'API n'est pas une option.

#scrapy #python #web-scraping #synchronisation-stocks #export-csv #automatisation

Questions fréquentes

Tout ce que vous devez savoir sur ce sujet.

Un projet PrestaShop ?

Discutons-en directement.

★★★★★

193 projets livrés

Gratuit & sans engagement — réponse sous 24h

Alexandre Carette

Alexandre Carette

Expert PrestaShop & Architecture E-commerce

Développeur PrestaShop depuis 2014, 193 projets livrés. Je conçois des architectures headless Nuxt + PrestaShop et des outils d'automatisation IA pour les e-commerçants.