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.
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.txtet 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 :
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.
Questions fréquentes
Tout ce que vous devez savoir sur ce sujet.
Un projet PrestaShop ?
Discutons-en directement.
193 projets livrés
Lire sur le blog

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.