Remplir une boutique en ligne à la main est la partie la plus coûteuse et la plus fastidieuse du lancement. Quand le fournisseur propose des milliers de références et que les prix changent chaque semaine, impossible de se passer d'automatisation. C'est là qu'entre en scène le parseur pour OpenCart — un programme qui collecte les données produits sur un site source (fournisseur, distributeur, marketplace) et les charge dans votre boutique : noms, descriptions, photos, caractéristiques et, surtout, prix correctement recalculés.
Dans cet article, nous verrons en quoi les versions d'OpenCart diffèrent du point de vue du scraping, quelles solutions prêtes à l'emploi existent, et nous écrirons notre propre scraper en Python qui collecte les produits sur le site d'un fournisseur et convertit la devise en parallèle.
Pourquoi la version d'OpenCart a de l'importance
Au bout du compte, le scraper écrit les données dans la base ou génère un fichier d'import, or la structure de la base et la logique des URL SEO diffèrent selon les versions d'OpenCart. L'ignorer, c'est la garantie d'obtenir des produits « cassés » ou des erreurs d'import.
OpenCart est apparu dès 2005 et a connu depuis plusieurs refontes majeures : la version 1.5 (2011) a apporté un nouvel éditeur de mise en page et une nouvelle administration, la branche 2.x (2014) une architecture modulaire et un système d'extensions amélioré, la version 3.0 (2017) le multi-boutique et la Marketplace, tandis que la branche 4.x est devenue la version actuelle avec une base de code modernisée et le retour d'OCMOD. Chacune de ces branches se rencontre encore sur des projets en production.
Voici ce que le scraper doit prendre en compte :
OpenCart 1.5.x — branche obsolète mais encore présente. Structure de tables simple, mais des noms de champs différents dans plusieurs tables. Les modules d'import prêts à l'emploi pour cette version ne sont quasiment plus maintenus.
OpenCart 2.x — la branche « ancienne » de travail la plus répandue. C'est là que vivent nombre de modules d'import éprouvés. Les URL SEO sont stockées dans la table url_alias.
OpenCart 3.x — la version la plus populaire pour les boutiques en production au moment de la rédaction. Structure proche de la 2.x, URL SEO également dans url_alias. La plupart des scrapers et modules prêts à l'emploi sont conçus précisément pour les branches 2.x–3.x.
OpenCart 4.x — la branche actuelle. Changement fondamental : les URL SEO ont migré de url_alias vers la table seo_url (avec rattachement au store_id et au language_id), et une partie des contrôleurs a changé. Un scraper écrit pour OC3 ne posera pas d'URL propres correctes sur OC4 sans modifications.
ocStore / LiveStore — des distributions russifiées basées sur OpenCart, populaires sur le web russophone. La structure des données correspond à la version d'OpenCart sous-jacente, mais les modules « clés en main » se comportent parfois différemment : testez-les toujours séparément.
Conclusion pratique : avant d'écrire ou d'acheter un scraper, notez la version exacte de la boutique (visible en bas de page dans l'administration ou dans l'
index.phpdu noyau) et le préfixe des tables de la base (par défautoc_, mais souvent personnalisé).
Solutions prêtes à l'emploi : ce qui existe sur le marché
Si le budget est limité et que la boutique est standard, il n'est peut-être pas nécessaire d'écrire votre propre scraper. Les outils prêts à l'emploi ne manquent pas et se répartissent en plusieurs catégories.
Modules d'import au sein d'OpenCart
Ce sont des extensions qui ne « parcourent » pas elles-mêmes les sites tiers, mais acceptent un fichier préparé (CSV / XLS / XLSX / YML / XML) et le chargent dans le catalogue :
- Export/Import Tool — module gratuit et populaire, capable d'importer jusqu'à plusieurs milliers de produits (les limites dépendent de l'hébergement). Bonne option pour démarrer.
- CSV Price Pro import/export — module payant (environ 1 950 ₽ par domaine), plus flexible pour le paramétrage de la correspondance des champs.
- anyCSV/XLS/YML PRO — puissant module commercial d'import CSV, XLS, XLSX, YML, CML pour OC 2–3.
- ScrapeSmart Catalog Import — module d'import/mise à jour massive du catalogue via une file AJAX (OpenCart, ocStore, LiveStore 3).
Le duo « le scraper produit un fichier → le module d'import le charge » est le plus fiable : vous voyez les données de vos propres yeux avant le chargement et ne cassez rien directement dans la base.
Scrapers-modules tout-en-un
Ce sont des solutions « tout en un » : elles scrapent les sites sources et chargent le résultat dans la boutique :
- Price Master — module complet avec scraper multithread intégré, traducteur, générateurs de texte par IA, planificateur et mise en correspondance flexible des données. La licence est délivrée pour un an et un seul domaine.
- « Postavchiki AOP » — l'un des modules de scraping et d'import les plus connus du web russophone : il crée les catégories, charge des milliers de produits avec descriptions, options, attributs et photos, et sait standardiser les attributs « à la volée ».
Logiciels externes et services cloud
- Datacol — logiciel de bureau avec des dizaines de configurations prêtes pour les boutiques populaires ; il collecte les données depuis les tarifs, les sites et les marketplaces et les exporte vers OpenCart (semi-automatiquement via fichier ou directement en base).
- ParseMX — scraper flexible pour synchroniser OpenCart avec les sites des fournisseurs : il copie la structure des catégories, traduit les textes, prend en charge tous les champs produit (y compris h1, title, SEO URL), fonctionne avec des proxys et met à jour prix et stocks.
Quand une solution prête à l'emploi ne convient-elle pas ? Quand le fournisseur a un balisage non trivial ou une protection anti-scraping, qu'il faut une logique de marge spécifique, une conversion de devises selon votre propre taux, un rattachement à votre structure de catégories ou une synchronisation régulière des stocks. Dans ces cas, on écrit un scraper sur mesure — c'est justement ce que nous allons faire.
Exemple : votre propre scraper avec import et conversion de devises
Étudions un scraper pédagogique en Python qui :
- parcourt le catalogue du fournisseur et collecte les fiches produits ;
- extrait le nom, le prix, la description, la référence et l'image ;
- convertit le prix de la devise du fournisseur vers celle de la boutique au taux actuel avec une marge définie ;
- importe les produits directement dans la base OpenCart.
Le code est fourni à des fins pédagogiques. Avant tout lancement sur une boutique en production, faites impérativement une sauvegarde de la base, vérifiez que le scraping du site source n'enfreint ni ses règles ni la loi, et testez le tout sur une copie de la boutique.
Stack et préparation
pip install requests beautifulsoup4 pymysql lxml
requests+beautifulsoup4— téléchargement et parsing du HTML ;pymysql— écriture dans la base OpenCart ;- le taux de change est récupéré via une API publique (dans l'exemple, celle de la Banque centrale de Russie ; remplaçable par n'importe quelle autre).
Étape 1. Le convertisseur de devises
D'abord un module séparé qui récupère le taux et recalcule le prix avec la marge. Le taux est mis en cache pour ne pas solliciter l'API à chaque produit.
import requests
import xml.etree.ElementTree as ET
from datetime import date
class CurrencyConverter:
"""Recupere le taux de la banque centrale et convertit le prix fournisseur dans la devise de la boutique."""
CBR_URL = "https://www.cbr.ru/scripts/XML_daily.asp"
def __init__(self, source_currency="USD", markup=1.20):
# markup=1.20 — marge de la boutique : +20 % au-dessus du taux
self.source_currency = source_currency
self.markup = markup
self._rate = None
self._rate_date = None
def _fetch_rate(self):
"""Recupere le taux de la devise voulue face au rouble depuis le XML de la banque centrale."""
resp = requests.get(self.CBR_URL, timeout=15)
resp.encoding = "windows-1251"
tree = ET.fromstring(resp.text)
for valute in tree.findall("Valute"):
if valute.find("CharCode").text == self.source_currency:
nominal = int(valute.find("Nominal").text)
value = float(valute.find("Value").text.replace(",", "."))
return value / nominal # taux pour 1 unite de devise
raise ValueError(f"Devise {self.source_currency} introuvable dans la reponse de la banque centrale")
@property
def rate(self):
# on met le taux en cache pour la date du jour
if self._rate is None or self._rate_date != date.today():
self._rate = self._fetch_rate()
self._rate_date = date.today()
return self._rate
def convert(self, amount):
"""Prix fournisseur -> prix boutique (en roubles) avec marge."""
if amount is None:
return None
price = float(amount) * self.rate * self.markup
return round(price, 2)
Étape 2. Le scraper des fiches fournisseur
Les sélecteurs (.product-card, .price, etc.) sont fictifs : pour chaque site source, on les détermine individuellement en ouvrant le code source de la page.
import time
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
class SupplierParser:
def __init__(self, base_url, converter, delay=1.0):
self.base_url = base_url
self.converter = converter
self.delay = delay # pause entre les requetes pour ne pas surcharger le site source
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (compatible; CatalogImporter/1.0)"
})
def _get_soup(self, url):
resp = self.session.get(url, timeout=20)
resp.raise_for_status()
return BeautifulSoup(resp.text, "lxml")
def parse_catalog(self, catalog_path, max_pages=5):
"""Collecte les liens produits sur les pages du catalogue paginé."""
product_urls = []
for page in range(1, max_pages + 1):
url = urljoin(self.base_url, f"{catalog_path}?page={page}")
soup = self._get_soup(url)
cards = soup.select(".product-card a.product-link")
if not cards:
break # plus de pages
for a in cards:
product_urls.append(urljoin(self.base_url, a["href"]))
time.sleep(self.delay)
return product_urls
def parse_product(self, url):
"""Analyse une fiche produit."""
soup = self._get_soup(url)
name = soup.select_one("h1.product-title")
price_raw = soup.select_one(".price .value")
description = soup.select_one(".product-description")
sku = soup.select_one(".sku")
image = soup.select_one(".product-gallery img")
# on nettoie le prix des espaces et symboles monetaires
price_source = None
if price_raw:
digits = "".join(ch for ch in price_raw.text if ch.isdigit() or ch == ".")
price_source = float(digits) if digits else None
time.sleep(self.delay)
return {
"name": name.text.strip() if name else "Sans nom",
"sku": sku.text.strip() if sku else "",
"description": description.decode_contents().strip() if description else "",
"image_url": urljoin(self.base_url, image["src"]) if image else "",
"price_source": price_source,
# c'est ici que se fait la conversion de devise :
"price": self.converter.convert(price_source),
"source_url": url,
}
Étape 3. Import dans la base OpenCart
C'est ici que ce qui a été dit au début entre en jeu : la structure dépend de la version. L'exemple vise OpenCart 3.x ; la différence clé pour OC4 est indiquée en commentaire.
import pymysql
from datetime import datetime
from transliterate import slugify # pip install transliterate
class OpenCartImporter:
def __init__(self, db_config, prefix="oc_", store_id=0, language_id=1):
self.conn = pymysql.connect(**db_config, charset="utf8mb4",
cursorclass=pymysql.cursors.DictCursor)
self.prefix = prefix
self.store_id = store_id
self.language_id = language_id
def _seo_keyword(self, name):
return slugify(name, "ru") or "product"
def import_product(self, p, category_id=None):
cur = self.conn.cursor()
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
px = self.prefix
# 1) table principale du produit
cur.execute(f"""
INSERT INTO {px}product
(model, sku, quantity, price, image, status,
date_added, date_modified, date_available)
VALUES (%s, %s, %s, %s, %s, 1, %s, %s, %s)
""", (p["sku"] or p["name"][:64], p["sku"], 100,
p["price"], "", now, now, now))
product_id = cur.lastrowid
# 2) description (nom, texte, meta) — pour chaque langue
cur.execute(f"""
INSERT INTO {px}product_description
(product_id, language_id, name, description,
meta_title, meta_description)
VALUES (%s, %s, %s, %s, %s, %s)
""", (product_id, self.language_id, p["name"],
p["description"], p["name"], p["name"]))
# 3) rattachement a la boutique
cur.execute(f"""
INSERT INTO {px}product_to_store (product_id, store_id)
VALUES (%s, %s)
""", (product_id, self.store_id))
# 4) rattachement a la categorie (si definie)
if category_id:
cur.execute(f"""
INSERT INTO {px}product_to_category (product_id, category_id)
VALUES (%s, %s)
""", (product_id, category_id))
# 5) SEO URL
keyword = f"{self._seo_keyword(p['name'])}-{product_id}"
# — OpenCart 3.x : table url_alias ---
cur.execute(f"""
INSERT INTO {px}url_alias (query, keyword)
VALUES (%s, %s)
""", (f"product_id={product_id}", keyword))
# — Pour OpenCart 4.x ce serait (table seo_url) :
# cur.execute(f'''INSERT INTO {px}seo_url
# (store_id, language_id, key, value, keyword)
# VALUES (%s, %s, "product_id", %s, %s)''',
# (self.store_id, self.language_id, product_id, keyword))
self.conn.commit()
return product_id
def close(self):
self.conn.close()
Étape 4. Assemblage final
def main():
converter = CurrencyConverter(source_currency="USD", markup=1.25)
parser = SupplierParser("https://supplier-example.com", converter, delay=1.0)
importer = OpenCartImporter(
db_config={
"host": "localhost",
"user": "db_user",
"password": "db_password",
"database": "opencart_db",
},
prefix="oc_",
store_id=0,
language_id=1,
)
product_urls = parser.parse_catalog("/catalog/category-1", max_pages=3)
print(f"Produits trouves : {len(product_urls)}")
imported = 0
for url in product_urls:
try:
product = parser.parse_product(url)
if product["price"] is None:
print(f"Ignore (pas de prix) : {url}")
continue
pid = importer.import_product(product, category_id=20)
imported += 1
print(f"[{imported}] {product['name']} — "
f"{product['price_source']} {converter.source_currency} "
f"-> {product['price']} RUB (ID {pid})")
except Exception as e:
print(f"Erreur sur {url} : {e}")
importer.close()
print(f"Termine. Importes : {imported}")
if __name__ == "__main__":
main()
Ce qui reste « hors champ » et compte dans un vrai projet
L'exemple pédagogique est volontairement simplifié. Dans un scraper de production, il faut en plus prévoir :
- Le téléchargement et le rattachement des images — télécharger les photos dans
image/catalog/...et enregistrer le chemin dansproduct.imageetoc_product_image. Les sites sources renvoient souvent un 403 en téléchargement direct : il faut alors un referer et des en-têtes corrects. - La déduplication — vérifier via
sku/modelsi le produit existe déjà en base et mettre à jour le prix/stock au lieu de créer des doublons. C'est aussi ce qui transforme un scraping ponctuel en synchronisation régulière. - Les attributs et options —
oc_product_attribute,oc_product_option, avec leur propre structure de relations. - Les catégories — création automatique de l'arborescence de catégories d'après la structure du site source (
oc_category,oc_category_description,oc_category_path). - La purge du cache OpenCart et la reconstruction des index SEO après un import massif.
- Le taux et la marge — les stocker en configuration et journaliser à quel taux chaque lot a été recalculé, pour que les prix restent reproductibles et transparents.
- L'éthique et la légalité — respecter le
robots.txt, des pauses raisonnables entre les requêtes et les conditions d'utilisation du site source.
Le même scraper en PHP — directement sur le serveur de la boutique
Python est pratique, mais exige un environnement dédié. Or OpenCart est lui-même écrit en PHP et tourne sur MySQL : on peut donc réaliser le scraper avec la même stack et l'exécuter directement sur l'hébergement de la boutique, sans installer Python ni runtime tiers. Les avantages de cette approche :
- rien à installer en plus — PHP est déjà sur le serveur ;
- on peut réutiliser le
config.phpd'OpenCart pour ne pas dupliquer les accès à la base ; - facile à planifier via cron pour une synchronisation régulière ;
- si besoin, le script peut se brancher au noyau d'OpenCart et écrire les produits via ses modèles plutôt que directement dans les tables.
Pour analyser le HTML, nous utilisons les classes natives DOMDocument + DOMXPath (sans bibliothèques tierces), cURL pour les requêtes et PDO pour la base.
Étape 1. Le convertisseur de devises
<?php
class CurrencyConverter
{
private const CBR_URL = 'https://www.cbr.ru/scripts/XML_daily.asp';
private ?float $rate = null;
private ?string $rateDate = null;
public function __construct(
private string $sourceCurrency = 'USD',
private float $markup = 1.20 // marge de la boutique : +20 % au-dessus du taux
) {}
private function fetchRate(): float
{
$xmlRaw = file_get_contents(self::CBR_URL);
// la reponse de la banque centrale est en windows-1251 — on la convertit en UTF-8
$xmlRaw = mb_convert_encoding($xmlRaw, 'UTF-8', 'windows-1251');
$xmlRaw = preg_replace('/encoding="windows-1251"/', 'encoding="UTF-8"', $xmlRaw);
$xml = new SimpleXMLElement($xmlRaw);
foreach ($xml->Valute as $valute) {
if ((string)$valute->CharCode === $this->sourceCurrency) {
$nominal = (int)$valute->Nominal;
$value = (float)str_replace(',', '.', (string)$valute->Value);
return $value / $nominal; // taux pour 1 unite de devise
}
}
throw new RuntimeException("Devise {$this->sourceCurrency} introuvable dans la reponse de la banque centrale");
}
public function getRate(): float
{
$today = date('Y-m-d');
if ($this->rate === null || $this->rateDate !== $today) {
$this->rate = $this->fetchRate();
$this->rateDate = $today;
}
return $this->rate;
}
public function convert(?float $amount): ?float
{
if ($amount === null) {
return null;
}
return round($amount * $this->getRate() * $this->markup, 2);
}
}
Étape 2. Le scraper des fiches fournisseur
<?php
class SupplierParser
{
public function __construct(
private string $baseUrl,
private CurrencyConverter $converter,
private float $delay = 1.0 // pause entre les requetes, en secondes
) {}
private function getHtml(string $url): string
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 20,
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; CatalogImporter/1.0)',
]);
$html = curl_exec($ch);
if ($html === false) {
throw new RuntimeException('cURL: ' . curl_error($ch));
}
curl_close($ch);
return $html;
}
private function makeXPath(string $html): DOMXPath
{
$doc = new DOMDocument();
libxml_use_internal_errors(true); // on ignore les erreurs du balisage « sale »
$doc->loadHTML('<?xml encoding="UTF-8">' . $html);
libxml_clear_errors();
return new DOMXPath($doc);
}
private function absolute(string $href): string
{
return str_starts_with($href, 'http') ? $href : rtrim($this->baseUrl, '/') . '/' . ltrim($href, '/');
}
/** Collecte les liens produits sur les pages du catalogue paginé. */
public function parseCatalog(string $catalogPath, int $maxPages = 5): array
{
$urls = [];
for ($page = 1; $page <= $maxPages; $page++) {
$html = $this->getHtml($this->absolute("{$catalogPath}?page={$page}"));
$xpath = $this->makeXPath($html);
$links = $xpath->query("//div[contains(@class,'product-card')]//a[contains(@class,'product-link')]");
if ($links->length === 0) {
break; // plus de pages
}
foreach ($links as $a) {
$urls[] = $this->absolute($a->getAttribute('href'));
}
usleep((int)($this->delay * 1_000_000));
}
return $urls;
}
/** Analyse une fiche produit. */
public function parseProduct(string $url): array
{
$xpath = $this->makeXPath($this->getHtml($url));
$text = fn(string $q) => trim($xpath->query($q)->item(0)?->textContent ?? '');
$name = $text("//h1[contains(@class,'product-title')]") ?: 'Sans nom';
$sku = $text("//*[contains(@class,'sku')]");
$description = $text("//*[contains(@class,'product-description')]");
$priceRaw = $text("//*[contains(@class,'price')]//*[contains(@class,'value')]");
$imgNode = $xpath->query("//*[contains(@class,'product-gallery')]//img")->item(0);
$imageUrl = $imgNode ? $this->absolute($imgNode->getAttribute('src')) : '';
// on nettoie le prix des espaces et symboles monetaires
$priceSource = null;
if ($priceRaw !== '') {
$digits = preg_replace('/[^0-9.]/', '', str_replace(',', '.', $priceRaw));
$priceSource = $digits !== '' ? (float)$digits : null;
}
usleep((int)($this->delay * 1_000_000));
return [
'name' => $name,
'sku' => $sku,
'description' => $description,
'image_url' => $imageUrl,
'price_source' => $priceSource,
// conversion de devise :
'price' => $this->converter->convert($priceSource),
'source_url' => $url,
];
}
}
Étape 3. Import dans la base OpenCart
<?php
class OpenCartImporter
{
private PDO $pdo;
public function __construct(
array $dbConfig,
private string $prefix = 'oc_',
private int $storeId = 0,
private int $languageId = 1
) {
$dsn = "mysql:host={$dbConfig['host']};dbname={$dbConfig['database']};charset=utf8mb4";
$this->pdo = new PDO($dsn, $dbConfig['user'], $dbConfig['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
}
private function seoKeyword(string $name): string
{
// slugification simplifiee pour l'URL propre
$slug = mb_strtolower(trim($name));
$slug = preg_replace('/[^a-z0-9\s-]/u', '', $slug);
$slug = preg_replace('/\s+/u', '-', $slug);
return $slug !== '' ? $slug : 'product';
}
public function importProduct(array $p, ?int $categoryId = null): int
{
$px = $this->prefix;
$now = date('Y-m-d H:i:s');
// 1) table principale du produit
$stmt = $this->pdo->prepare("
INSERT INTO {$px}product
(model, sku, quantity, price, image, status,
date_added, date_modified, date_available)
VALUES (:model, :sku, 100, :price, '', 1, :added, :modified, :available)
");
$stmt->execute([
':model' => $p['sku'] ?: mb_substr($p['name'], 0, 64),
':sku' => $p['sku'],
':price' => $p['price'],
':added' => $now,
':modified' => $now,
':available' => $now,
]);
$productId = (int)$this->pdo->lastInsertId();
// 2) description (nom, texte, meta)
$this->pdo->prepare("
INSERT INTO {$px}product_description
(product_id, language_id, name, description, meta_title, meta_description)
VALUES (?, ?, ?, ?, ?, ?)
")->execute([
$productId, $this->languageId, $p['name'],
$p['description'], $p['name'], $p['name'],
]);
// 3) rattachement a la boutique
$this->pdo->prepare("
INSERT INTO {$px}product_to_store (product_id, store_id) VALUES (?, ?)
")->execute([$productId, $this->storeId]);
// 4) rattachement a la categorie
if ($categoryId !== null) {
$this->pdo->prepare("
INSERT INTO {$px}product_to_category (product_id, category_id) VALUES (?, ?)
")->execute([$productId, $categoryId]);
}
// 5) SEO URL
$keyword = $this->seoKeyword($p['name']) . '-' . $productId;
// — OpenCart 3.x : table url_alias ---
$this->pdo->prepare("
INSERT INTO {$px}url_alias (query, keyword) VALUES (?, ?)
")->execute(["product_id={$productId}", $keyword]);
// — Pour OpenCart 4.x ce serait (table seo_url) :
// INSERT INTO {$px}seo_url (store_id, language_id, `key`, `value`, keyword)
// VALUES (:store, :lang, 'product_id', :pid, :keyword)
return $productId;
}
}
Étape 4. Point d'entrée et lancement via cron
<?php
require 'CurrencyConverter.php';
require 'SupplierParser.php';
require 'OpenCartImporter.php';
$converter = new CurrencyConverter('USD', 1.25);
$parser = new SupplierParser('https://supplier-example.com', $converter, 1.0);
// les acces peuvent etre externalises dans un config ou tires du config.php d'OpenCart
$importer = new OpenCartImporter([
'host' => 'localhost',
'user' => 'db_user',
'password' => 'db_password',
'database' => 'opencart_db',
], prefix: 'oc_', storeId: 0, languageId: 1);
$urls = $parser->parseCatalog('/catalog/category-1', maxPages: 3);
echo 'Produits trouves : ' . count($urls) . PHP_EOL;
$imported = 0;
foreach ($urls as $url) {
try {
$product = $parser->parseProduct($url);
if ($product['price'] === null) {
echo "Ignore (pas de prix) : {$url}" . PHP_EOL;
continue;
}
$pid = $importer->importProduct($product, categoryId: 20);
$imported++;
printf("[%d] %s — %s USD -> %s RUB (ID %d)%s",
$imported, $product['name'], $product['price_source'], $product['price'], $pid, PHP_EOL);
} catch (Throwable $e) {
echo "Erreur sur {$url} : {$e->getMessage()}" . PHP_EOL;
}
}
echo "Termine. Importes : {$imported}" . PHP_EOL;
Programmer un lancement automatique quotidien tient en une ligne de crontab :
# chaque jour a 4h00, on synchronise le catalogue avec le fournisseur
0 4 * * * /usr/bin/php /var/www/opencart/parser/run.php >> /var/www/opencart/parser/parser.log 2>&1
Pour récupérer les accès à la base directement depuis OpenCart :
require '/path/to/opencart/config.php';— les constantesDB_HOSTNAME,DB_USERNAME,DB_PASSWORD,DB_DATABASEetDB_PREFIXdeviennent alors disponibles, et il n'est plus nécessaire de dupliquer les mots de passe dans le scraper.
Toutes les réserves de la section « hors champ » (images, déduplication, attributs, cache, légalité) valent tout autant pour la version PHP.
Conclusion
Pour une boutique standard, le duo « scraper prêt à l'emploi + module d'import » suffit souvent : Export/Import Tool, CSV Price Pro, Price Master, Datacol ou ParseMX couvrent la majorité des scénarios. Mais dès qu'apparaît un site source atypique, une logique de marge spécifique, une conversion de devises au taux du jour ou une synchronisation régulière des stocks pour une version précise d'OpenCart, il est plus rentable et plus fiable de commander un scraper sur mesure.
Besoin d'une solution de scraping de boutiques en ligne ?
Si vous devez remplir ou synchroniser une boutique sur OpenCart (ou toute autre plateforme), collecter des données sur les sites de vos fournisseurs, mettre en place une mise à jour automatique des prix avec conversion de devises — contactez-nous. Nous concevrons et déploierons un scraper adapté à votre version d'OpenCart et à vos besoins.