Files
scrap/BACKMARKET_ANALYSIS.md
2026-01-13 19:49:04 +01:00

12 KiB
Executable File

Analyse Store Backmarket

Date: 2026-01-13 Auteur: PriceWatch Development Team

Résumé Exécutif

Backmarket.fr est un marketplace de produits reconditionnés (smartphones, laptops, tablets, etc.). Le parsing est très fiable grâce à l'utilisation extensive de JSON-LD schema.org, mais nécessite Playwright obligatoire en raison de la protection anti-bot.

Score de parsing: (5/5) - Excellent Difficulté d'accès: 🔒🔒🔒 (3/5) - Anti-bot fort


🎯 Spécificités Backmarket

1. Marketplace de Reconditionné

Contrairement à Amazon ou Cdiscount qui vendent du neuf, Backmarket vend exclusivement du reconditionné.

Implications:

  • Chaque produit a plusieurs offres avec différents états/conditions
  • Prix variable selon la condition choisie (Correct, Bon, Excellent, etc.)
  • Le grade/condition doit être extrait et stocké dans specs["Condition"]

Grades Backmarket:

  • Correct - État correct avec traces d'usage visibles
  • Bon - Quelques rayures légères
  • Très bon - Très peu de rayures
  • Excellent - Quasiment aucune trace d'usage
  • Comme neuf - État neuf

2. Protection Anti-bot Forte

HTTP simple ne fonctionne PAS - retourne 403 Forbidden Playwright OBLIGATOIRE pour récupérer le contenu

# ❌ NE FONCTIONNE PAS
result = fetch_http("https://www.backmarket.fr/fr-fr/p/iphone-15-pro")
# → 403 Forbidden

# ✅ FONCTIONNE
result = fetch_playwright("https://www.backmarket.fr/fr-fr/p/iphone-15-pro", headless=True)
# → 200 OK avec contenu complet

Temps de chargement: ~2-3 secondes avec Playwright


📊 Structure HTML & Extraction

JSON-LD Schema.org (Source Prioritaire)

Backmarket utilise schema.org Product de manière complète et fiable.

Exemple de JSON-LD:

{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "iPhone 15 Pro",
  "image": "https://d2e6ccujb3mkqf.cloudfront.net/...",
  "offers": {
    "@type": "Offer",
    "price": "571.00",
    "priceCurrency": "EUR",
    "availability": "https://schema.org/InStock"
  }
}

Avantages:

  • Données structurées et stables
  • Prix toujours au format numérique propre
  • Devise explicite
  • URL d'image de haute qualité

Extraction prioritaire dans BackmarketStore._extract_json_ld():

def _extract_json_ld(self, soup: BeautifulSoup) -> dict:
    """Extrait les données depuis JSON-LD schema.org."""
    json_ld_scripts = soup.find_all("script", {"type": "application/ld+json"})

    for script in json_ld_scripts:
        data = json.loads(script.string)
        if data.get("@type") == "Product":
            return {
                "name": data.get("name"),
                "price": float(data["offers"]["price"]),
                "priceCurrency": data["offers"]["priceCurrency"],
                "images": [data.get("image")]
            }

Sélecteurs CSS (Fallback)

Si JSON-LD n'est pas disponible, on utilise des sélecteurs CSS relativement stables.

Sélecteurs identifiés:

Champ Sélecteur Stabilité
Titre h1.heading-1 Stable
Prix div[data-test='price'] Stable
Condition button[data-test='condition-button'] Stable
Images img[alt] Moyen (filtrer par nom produit)
Stock button[data-test='add-to-cart'] Stable
Specs dl > dt, dd Moyen
Category nav[aria-label='breadcrumb'] a Moyen

Stabilité des sélecteurs: Bonne - Backmarket utilise des classes sémantiques (heading-1) et des attributs data-test qui sont plus stables que les classes générées aléatoirement.


🔧 Implémentation Technique

URL Pattern & SKU Extraction

Format URL: https://www.backmarket.fr/{locale}/p/{slug}

Exemples:

  • https://www.backmarket.fr/fr-fr/p/iphone-15-pro → SKU = "iphone-15-pro"
  • https://www.backmarket.com/en-us/p/macbook-air-m2 → SKU = "macbook-air-m2"

Regex d'extraction:

def extract_reference(self, url: str) -> Optional[str]:
    match = re.search(r"/p/([a-z0-9-]+)", url, re.IGNORECASE)
    if match:
        return match.group(1)
    return None

Caractéristiques du slug:

  • Format kebab-case: product-name-variant
  • Peut contenir chiffres: iphone-15-pro, galaxy-s23
  • Stable dans le temps (identifiant produit)

Canonicalization

On retire les paramètres de query et le fragment car ils ne changent pas le produit.

def canonicalize(self, url: str) -> str:
    parsed = urlparse(url)
    return f"{parsed.scheme}://{parsed.netloc}{parsed.path}"

Exemples:

  • https://www.backmarket.fr/fr-fr/p/iphone-15-pro?color=blackhttps://www.backmarket.fr/fr-fr/p/iphone-15-pro
  • https://www.backmarket.fr/fr-fr/p/iphone-15-pro#specshttps://www.backmarket.fr/fr-fr/p/iphone-15-pro

📈 Tests & Validation

Test Coverage

Tests unitaires: 19 tests Tests fixtures: 11 tests Total: 30 tests - 100% PASS

Coverage du store: 85%

Fichiers de tests:

  • tests/stores/test_backmarket.py - Tests unitaires (match, canonicalize, extract_reference, parse)
  • tests/stores/test_backmarket_fixtures.py - Tests avec HTML réel

Fixture Réelle

Produit: iPhone 15 Pro (reconditionné) Fichier: pricewatch/app/stores/backmarket/fixtures/backmarket_iphone15pro.html Taille: 1.5 MB Date capture: 2026-01-13 Méthode: Playwright (obligatoire)

Données extraites de la fixture:

{
  "source": "backmarket",
  "title": "iPhone 15 Pro",
  "price": 571.0,
  "currency": "EUR",
  "reference": "iphone-15-pro",
  "images": ["https://d2e6ccujb3mkqf.cloudfront.net/..."],
  "is_complete": true
}

⚖️ Comparaison avec Autres Stores

Aspect Amazon Cdiscount Backmarket
Anti-bot Faible Fort (Baleen) Fort (Cloudflare)
Méthode HTTP OK Playwright Playwright
JSON-LD Partiel ✗ Non ✓ Oui (complet)
Sélecteurs Stables (IDs) Instables Stables (data-test)
SKU format /dp/{ASIN} /f-{cat}-{SKU} /p/{slug}
Parsing fiabilité
Vitesse fetch Rapide (200ms) Lent (2-3s) Lent (2-3s)
Particularité - Prix dynamiques Reconditionné

Conclusion: Backmarket est le store le plus fiable pour le parsing grâce au JSON-LD, mais nécessite Playwright comme Cdiscount.


Avantages de Backmarket

  1. JSON-LD schema.org complet → Parsing ultra-fiable
  2. Classes CSS stables → Moins de casse que Cdiscount
  3. URL propre et prévisible → SKU facile à extraire
  4. Format prix standardisé → Toujours numérique dans JSON-LD
  5. Sélecteurs data-test → Conçus pour être stables

Inconvénients & Défis

1. Protection Anti-bot Obligatoire

Impact:

  • ⏱️ Temps de fetch: ~2-3 secondes (vs 200ms pour HTTP)
  • 💰 Coût: Plus de ressources CPU/mémoire
  • 🐳 Docker: Nécessite installation de Playwright browsers

Solution: Utiliser le cache agressivement, éviter les fetch répétés.

2. Prix Variable selon Condition

Le même produit peut avoir 5-10 prix différents selon l'état.

Problème: Quel prix extraire ? Solution actuelle: On extrait le prix de l'offre par défaut sélectionnée (souvent "Excellent")

Amélioration future: Extraire toutes les offres avec leurs conditions et prix.

3. Stock Complexe

Le stock dépend de l'offre ET de la condition choisie.

Problème: Un produit peut être "en stock" dans une condition mais "rupture" dans une autre. Solution actuelle: On extrait le stock de l'offre par défaut.

4. 404 Fréquents

Les produits reconditionnés ont un stock limité et les URLs peuvent devenir invalides rapidement.

Exemple testé:

  • https://www.backmarket.fr/fr-fr/p/samsung-galaxy-s23 → 404
  • https://www.backmarket.fr/fr-fr/p/apple-macbook-air-133-pouces-m2-2022 → 404

Impact: Plus de gestion d'erreurs nécessaire dans le pipeline.


🎓 Recommandations

Pour le Développement

  1. Toujours utiliser Playwright pour Backmarket - Ne jamais tenter HTTP
  2. Prioriser JSON-LD - C'est la source la plus fiable
  3. Extraire la condition - Ajouter dans specs["Condition"]
  4. Gérer les 404 gracefully - Produits limités en stock
  5. Cache long - Minimiser les appels Playwright coûteux

Pour les Tests

  1. Maintenir les fixtures à jour - Les URLs expirent vite
  2. Tester avec différentes catégories - Smartphones, laptops, tablets
  3. Tester les différentes conditions - Prix varie selon état
  4. Tester les 404 - Cas fréquent pour le reconditionné

Pour la Production

  1. Monitoring des 404 - Alertes sur produits devenus indisponibles
  2. Rotation des proxies - Si scraping intensif
  3. Rate limiting - Respecter le site (2-3s entre requêtes minimum)
  4. Cache agressif - Playwright coûte cher en ressources

📝 Exemples d'Utilisation

Scraping Simple

from pricewatch.app.scraping.pw_fetch import fetch_playwright
from pricewatch.app.stores.backmarket.store import BackmarketStore

url = "https://www.backmarket.fr/fr-fr/p/iphone-15-pro"

# Fetch avec Playwright (obligatoire)
result = fetch_playwright(url, headless=True, timeout_ms=60000)

# Parse
store = BackmarketStore()
snapshot = store.parse(result.html, url)

print(f"Title: {snapshot.title}")
print(f"Price: {snapshot.price} {snapshot.currency}")
print(f"Condition: {snapshot.specs.get('Condition', 'N/A')}")
print(f"Complete: {snapshot.is_complete()}")

Détection Automatique

from pricewatch.app.core.registry import StoreRegistry

url = "https://www.backmarket.fr/fr-fr/p/iphone-15-pro"

# Détection automatique du store
store = StoreRegistry.detect(url)
print(f"Store détecté: {store.store_id}")  # → "backmarket"
print(f"Score: {store.match(url)}")  # → 0.9

Pipeline Complet

from pricewatch.app.cli.main import scrape_url

url = "https://www.backmarket.fr/fr-fr/p/iphone-15-pro"

# Pipeline complet: detect → fetch → parse → save
snapshot = scrape_url(
    url,
    use_playwright=True,  # Obligatoire pour Backmarket
    save_html=True,
    save_screenshot=True
)

print(f"✓ Scraped: {snapshot.title} - {snapshot.price} {snapshot.currency}")

🔍 Points d'Attention pour Debug

Si le parsing échoue:

  1. Vérifier que Playwright est utilisé - HTTP ne fonctionne jamais
  2. Vérifier le code status - Peut être 404 si produit épuisé
  3. Inspecter le JSON-LD - C'est la source prioritaire
  4. Vérifier la condition - Prix peut varier selon offre sélectionnée
  5. Logs détaillés - Activer --debug pour voir les erreurs

Exemple de debug:

# Activer les logs détaillés
export PRICEWATCH_LOG_LEVEL=DEBUG

# Scraper avec sauvegarde HTML
pricewatch fetch https://www.backmarket.fr/fr-fr/p/iphone-15-pro \
  --playwright \
  --save-html \
  --debug

📚 Ressources

Documentation officielle: https://www.backmarket.fr Fichiers du projet:

  • pricewatch/app/stores/backmarket/store.py - Implémentation
  • pricewatch/app/stores/backmarket/selectors.yml - Sélecteurs CSS
  • pricewatch/app/stores/backmarket/fixtures/README.md - Documentation des fixtures
  • tests/stores/test_backmarket.py - Tests unitaires
  • tests/stores/test_backmarket_fixtures.py - Tests avec HTML réel

Tests:

# Tous les tests Backmarket
pytest tests/stores/test_backmarket*.py -v

# Avec coverage
pytest tests/stores/test_backmarket*.py --cov=pricewatch.app.stores.backmarket

🎉 Conclusion

Backmarket est un excellent store à supporter pour PriceWatch:

  • Parsing très fiable grâce au JSON-LD
  • Sélecteurs stables (data-test, classes sémantiques)
  • Tests complets (30 tests, 100% pass)
  • ⚠️ Nécessite Playwright (coût en performance)
  • ⚠️ URLs peuvent expirer (stock limité)

Score final: (5/5) pour la fiabilité du parsing.

Recommandation: Store prioritaire à maintenir pour le marché du reconditionné.