Files
scrap/verif_amazon.md
T
Gilles Soulier e18976ad51 before maj scrap
2026-01-18 07:38:37 +01:00

1342 lines
47 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Vérification Scraping Amazon - Analyse des erreurs
**Date d'analyse**: 2026-01-18
---
# Résumé exécutif des bugs identifiés
## Bugs récurrents (P1 - Priorité haute)
| # | Bug | Occurrences | Cause racine |
|---|-----|-------------|--------------|
| 1 | **MSRP incorrect** | 5/8 produits | Sélecteur trop large capture prix de produits sponsorisés/recommandés |
| 1b | **MSRP = prix bas 30j** | 1/8 produits | `select_one()` prend le 1er prix barré (prix bas 30j) au lieu du 2ème (MSRP) |
| 2 | **Stock "Inconnu" affiché** | 3/8 produits | Parser ne reconnaît pas "expédié sous" + UI affiche "Inconnu" au lieu de masquer |
| 3 | **"EVOL. 30 JOURS" affiché** | Tous produits | Donnée calculée affichée alors qu'elle n'existe pas sur Amazon |
## Bugs ponctuels (P2)
| # | Bug | Produit | Cause |
|---|-----|---------|-------|
| 4 | Amazon Choice faux positif | Produit 3 | Détection sur élément vide |
| 5 | Type "Ecran" pour carte graphique | Produit 4 | Classification mots-clés incorrecte |
| 6 | Type "PC portable" pour RAM | Produit 6 | Classification mots-clés incorrecte |
## Notes importantes
- **Prix occasion** : Ne pas tenir compte des prix occasion (ex: 245€ vs 223€ neuf sur produit 6)
- **Différence HTTP/Playwright** : Le HTML diffère selon la méthode de fetch, impactant l'extraction MSRP
---
# Erreur Playwright détectée
## Message d'erreur
```
Erreur: Erreur Playwright: BrowserType.launch: Executable doesn't exist at /root/.cache/ms-playwright/chromium_headless_shell-1200
```
## Impact
Quand Playwright échoue, le système bascule sur HTTP. Le HTML retourné par HTTP diffère de celui de Playwright :
- **HTTP** : Contient plus de sections sponsorisées/recommandations avec des prix barrés
- **Playwright** : HTML plus "propre" après exécution JavaScript
Cela explique pourquoi certains MSRP incorrects sont capturés : le fallback HTTP expose des prix de produits tiers que Playwright aurait masqués ou chargés différemment.
## Correction requise
```bash
# Installer les navigateurs Playwright
playwright install
```
---
# Défauts globaux (tous produits)
## ❌ ERREUR UI: "EVOL. 30 JOURS" ne devrait pas être affiché
**Problème**: Le Web UI affiche "EVOL. 30 JOURS: +0.0%" (ou autre valeur) sur tous les produits, alors que cette donnée **n'existe pas sur Amazon**.
**Règle** : Seules les données scrapées depuis la source doivent être affichées dans le bloc produit. Une donnée absente de la source = ne pas afficher.
**Distinction importante** :
- ❌ "EVOL. 30 JOURS" → Donnée calculée, **non présente sur Amazon****Ne pas afficher**
- ✅ Graphique historique → Basé sur les scrapes passés de PriceWatch → **Légitime**
**Correction requise** : Supprimer la ligne "EVOL. 30 JOURS" du bloc d'informations produit.
---
---
# Produit 6 : Corsair Vengeance SODIMM 32Go
**URL**: https://www.amazon.fr/dp/B08GSTF5NJ?th=1
## Comparaison: Données Amazon vs Données Scrapées
| Champ | Valeur Amazon (source) | Valeur Scrapée (Web UI) | Statut |
|-------|------------------------|-------------------------|--------|
| **Titre** | Corsair Vengeance SODIMM 32Go (2x16Go) DDR4 3200MHz C22... | Corsair Vengeance SODIMM 32Go (2x16Go) DDR4 3200MHz C22... | ✅ OK |
| **Prix** | 245,00€ (occasion) / 223,02€ (neuf) | 223,02 € | ⚠️ VOIR NOTE |
| **Prix conseillé (MSRP)** | Non affiché sur Amazon | 606,99 € | ❌ ERREUR |
| **Stock** | Non affiché explicitement | "Inconnu" | ⚠️ À NE PAS AFFICHER |
| **Note** | 4,7 (8 626) | 4,7 (8 626) | ✅ OK |
| **Choix Amazon** | Non visible sur capture | Oui | ⚠️ À VÉRIFIER |
| **Référence (ASIN)** | B08GSTF5NJ | B08GSTF5NJ | ✅ OK |
| **Image principale** | Image RAM Corsair | Image RAM Corsair | ✅ OK |
| **Type** | - | PC portable | ❌ ERREUR |
## Défauts identifiés
### 1. ❌ ERREUR: MSRP incorrect (606,99 €) - Capture de prix tiers
**Problème**: Le Web UI affiche "PRIX CONSEILLÉ: 606,99€" alors qu'Amazon n'affiche aucun prix conseillé pour ce produit.
**Cause racine identifiée** :
Le sélecteur MSRP capture un prix barré d'un **produit tiers** dans la section "CardInstance" (recommandations).
```
Source HTML (HTTP): #CardInstanceZ_u0EeRMlrOaTZL9bV_a9A
Prix capturé: €606,99 (produit recommandé, pas le produit principal)
```
**Note** : Le HTML diffère entre HTTP et Playwright. En HTTP, le prix 606,99€ est trouvé. En Playwright, le parser retourne `None`.
**Lien avec erreur Playwright** : L'erreur `Executable doesn't exist at /root/.cache/ms-playwright/chromium_headless_shell-1200` force le fallback HTTP, qui capture ce prix incorrect depuis la section recommandations.
**Même cause récurrente** : Le sélecteur `span.a-text-price span.a-offscreen` n'est pas restreint au bloc prix principal.
---
### 2. ⚠️ NOTE: Prix neuf vs occasion
**Observation** : Amazon affiche en évidence le prix **occasion** (245,00€) alors que le prix **neuf** (223,02€) est moins visible.
Le parser capture correctement le prix neuf (223,02€), ce qui est le comportement souhaité.
**Pas une erreur**, mais une subtilité de mise en page Amazon à connaître.
---
### 3. ❌ ERREUR: Type "PC portable" incorrect
**Problème** : Le produit est de la **RAM SODIMM** (pour portables), pas un PC portable lui-même.
**Cause** : Le système de classification a probablement détecté "Ordinateur Portable" dans le titre et a mal catégorisé.
---
### 4. ⚠️ Stock "Inconnu" ne devrait pas être affiché
Comme établi précédemment, si Amazon n'affiche pas de stock → ne pas afficher "Inconnu".
---
## Résumé produit 6
| Priorité | Problème | Récurrent ? |
|----------|----------|-------------|
| 🟠 P1 | MSRP incorrect (donnée historique corrompue) | ✅ Oui |
| 🟡 P2 | Type "PC portable" au lieu de "RAM" | 🆕 Erreur classification |
| 🟡 P2 | Stock "Inconnu" affiché inutilement | ✅ Oui |
---
---
# Produit 5 : Samsung SSD 9100 Pro 2To
**URL**: https://www.amazon.fr/dp/B0DWFLPMM5?th=1
## Comparaison: Données Amazon vs Données Scrapées
| Champ | Valeur Amazon (source) | Valeur Scrapée (Web UI) | Statut |
|-------|------------------------|-------------------------|--------|
| **Titre** | Samsung SSD Interne 9100 Pro, NVMe 2.0 PCIe 5.0x4, Capacité 2To... | Samsung SSD Interne 9100 Pro, NVMe 2.0 PCIe 5.0x4, Capacité 2To... | ✅ OK |
| **Prix** | 249,99 € | 249,99 € | ✅ OK |
| **Prix conseillé (MSRP)** | 329,99 € | 329,99 € | ✅ OK |
| **Réduction** | -24 % | -24 % | ✅ OK |
| **Stock** | Non affiché explicitement (mais bouton "Ajouter au panier" présent) | "Inconnu" | ⚠️ AMÉLIORABLE |
| **Note** | 4,7 (966) | 4,7 (966) | ✅ OK |
| **Choix Amazon** | Non visible sur capture | Oui | ⚠️ À VÉRIFIER |
| **Référence (ASIN)** | B0DWFLPMM5 | B0DWFLPMM5 | ✅ OK |
| **Image principale** | Image SSD Samsung | Image SSD Samsung | ✅ OK |
| **Catégorie** | SSD | SSD | ✅ OK |
## Défauts identifiés
### 1. ⚠️ AVERTISSEMENT: Stock "Inconnu" alors que produit disponible
**Problème**: Le stock est affiché comme "Inconnu" alors que le produit est clairement disponible (bouton "Ajouter au panier", livraison Prime).
**Données HTML** :
```
#availability: "" (vide)
#availability span: "" (vide)
Bouton "Ajouter au panier": Présent
Livraison Prime: Présent
```
**Cause racine** :
Amazon n'affiche pas toujours "En stock" explicitement. Pour certains produits, le `#availability` est vide mais le produit est disponible.
**Amélioration proposée** :
```python
# Si #availability est vide mais bouton panier présent → IN_STOCK
if not text and soup.select_one('#add-to-cart-button'):
return StockStatus.IN_STOCK, "Disponible (panier)", True
```
---
### 2. ✅ MSRP correct
Ce produit confirme que le MSRP fonctionne quand il est réellement affiché sur la page (329,99€ barré visible sur Amazon).
---
## Résumé produit 5
| Priorité | Problème | Récurrent ? |
|----------|----------|-------------|
| 🟡 P2 | Stock "Inconnu" malgré disponibilité (pattern #availability vide) | 🆕 Nouveau pattern |
**Note positive** : Prix, MSRP (-24%), note et catégorie sont tous corrects.
---
---
# Produit 4 : MSI NVIDIA GeForce RTX 5060 Ti
**URL**: https://www.amazon.fr/dp/B0F32N1ZGH
## Comparaison: Données Amazon vs Données Scrapées
| Champ | Valeur Amazon (source) | Valeur Scrapée (Web UI) | Statut |
|-------|------------------------|-------------------------|--------|
| **Titre** | MSI NVIDIA GeForce RTX 5060 Ti 16G Inspire 2X OC Carte Graphique 16 Go GDDR7... | MSI NVIDIA GeForce RTX 5060 Ti 16G Inspire 2X OC Carte Graphique 16 Go GDDR... | ✅ OK |
| **Prix** | 509,00 € | 509 € | ✅ OK |
| **Prix conseillé (MSRP)** | Non affiché sur Amazon | 102,99 € | ❌ ERREUR |
| **Stock** | "En stock" | "En stock" | ✅ OK |
| **Note** | 4,6 (211) | 4,6 (211) | ✅ OK |
| **Choix Amazon** | Oui (badge visible) | Oui | ✅ OK |
| **Référence (ASIN)** | B0F32N1ZGH | B0F32N1ZGH | ✅ OK |
| **Image principale** | Image carte graphique MSI | Image carte graphique MSI | ✅ OK |
| **Catégorie** | Cartes graphiques | Informatique | ✅ OK |
| **Type** | - | Ecran | ❌ ERREUR |
## Défauts identifiés
### 1. ❌ ERREUR: MSRP incorrect (102,99 €)
**Problème**: Le Web UI affiche "PRIX CONSEILLÉ: 102,99€" pour une carte graphique à 509€. C'est absurde (MSRP < prix actuel).
**Cause racine identifiée** :
Le prix 102,99€ provient d'un **produit sponsorisé** affiché sur la page.
```
Source HTML: #sp_detail_B08ZCHF59L (produit sponsorisé)
```
**Même problème récurrent** : Le sélecteur MSRP capture des prix barrés de produits tiers (sponsorisés, similaires, etc.).
---
### 2. ❌ ERREUR: Type "Ecran" incorrect
**Problème**: Le produit est une **carte graphique**, pas un écran. La classification automatique est erronée.
**Cause probable** : Le système de classification par mots-clés a mal catégorisé le produit.
---
## Résumé produit 4
| Priorité | Problème | Récurrent ? |
|----------|----------|-------------|
| 🟠 P1 | MSRP incorrect (102,99€ d'un produit sponsorisé) | ✅ Oui (3ème occurrence) |
| 🟡 P2 | Type "Ecran" au lieu de "Carte graphique" | 🆕 Erreur classification |
---
---
# Produit 3 : ASUS TUF Gaming A16
**URL**: https://www.amazon.fr/dp/B0DQ8M74KL?th=1
## Comparaison: Données Amazon vs Données Scrapées
| Champ | Valeur Amazon (source) | Valeur Scrapée (Web UI) | Statut |
|-------|------------------------|-------------------------|--------|
| **Titre** | ASUS TUF Gaming A16-TUF608UH-RV054W 16 Pouces FHD Plus 165Hz... | ASUS TUF Gaming A16-TUF608UH-RV054W 16 Pouces FHD Plus 165Hz... | ✅ OK |
| **Prix** | 1 259,99 € | 1 259,99 € | ✅ OK |
| **Prix conseillé (MSRP)** | 1 699,99 € | 1 699,99 € | ✅ OK |
| **Réduction** | -26 % | -26 % | ✅ OK |
| **Stock** | "En stock" | "En stock" | ✅ OK |
| **Note** | 4,7 (7) | 4,7 (7) | ✅ OK |
| **Choix Amazon** | ❌ Non (c'est "Exclusivité Amazon") | Oui | ❌ ERREUR |
| **Référence (ASIN)** | B0DQ8M74KL | B0DQ8M74KL | ✅ OK |
| **Image principale** | Image laptop ASUS TUF | Image laptop ASUS TUF | ✅ OK |
| **Catégorie** | Ordinateurs portables classiques | Informatique | ✅ OK |
| **Type** | - | PC portable | ✅ OK |
## Défauts identifiés
### 1. ❌ ERREUR: "Choix Amazon" faux positif
**Problème**: Le Web UI affiche "CHOIX AMAZON: Oui" alors qu'Amazon affiche **"Exclusivité Amazon"** (badge différent).
**Données HTML** :
```
#acBadge_feature_div: "" (vide)
Badge réel: "Exclusivité Amazon" (classe: ae-badge-rectangle-desktop)
```
**Cause racine** :
La méthode `_extract_amazon_choice()` détecte l'élément `#acBadge_feature_div` qui existe dans le DOM mais est **vide**. Le parser retourne `True` simplement parce que l'élément existe.
**Fichier concerné**: [store.py:496-520](pricewatch/app/stores/amazon/store.py#L496-L520) - méthode `_extract_amazon_choice()`
**Correction proposée** :
```python
def _extract_amazon_choice(self, soup, debug):
for selector in selectors:
element = soup.select_one(selector)
if element:
text = element.get_text(strip=True)
# Ne retourner True que si le texte contient vraiment "Choix d'Amazon"
if not text:
continue # Élément vide = pas de badge
if "choix d'amazon" in text.lower() or "amazon's choice" in text.lower():
return True, text
return None, None
```
---
### 2. ️ OBSERVATION: Badge "Exclusivité Amazon" non capturé
**Opportunité** : Ajouter un champ `amazon_exclusive` pour capturer ce badge distinct.
```python
# Nouveau sélecteur
amazon_exclusive:
- "span.ae-badge-text-desktop"
- ".badge-wrapper:-soup-contains('Exclusivité')"
```
---
## Résumé produit 3
| Priorité | Problème | Récurrent ? |
|----------|----------|-------------|
| 🟡 P2 | Choix Amazon faux positif (élément vide) | 🆕 Nouveau pattern |
| 🟢 P3 | Badge "Exclusivité Amazon" non capturé | Opportunité |
**Note positive** : Ce produit montre que le MSRP est correctement capturé quand il existe réellement sur la page produit (1699,99€). Le problème MSRP des produits 1 et 2 vient bien de prix barrés de produits tiers.
---
---
# Produit 2 : Crucial Basics DDR4 8Go
**URL**: https://www.amazon.fr/dp/B0CWLSQ8FS
## Comparaison: Données Amazon vs Données Scrapées
| Champ | Valeur Amazon (source) | Valeur Scrapée (Web UI) | Statut |
|-------|------------------------|-------------------------|--------|
| **Titre** | Crucial Basics Mémoire pour Ordinateur de Bureau DDR4 3200 MT/s CL22 UDIMM 288 Broches 1,2 V 8 Go | Crucial Basics Mémoire pour Ordinateur de Bureau DDR4 3200 MT/s CL22 UDIMM 28... | ✅ OK |
| **Prix** | 80,00 € | 80 € | ✅ OK |
| **Prix conseillé (MSRP)** | Non affiché sur Amazon | 606,99 € | ❌ ERREUR |
| **Stock** | "Habituellement expédié sous 5 à 6 jours" | "Inconnu" | ❌ ERREUR |
| **Note** | 3,7 (27) | 3,7 (27) | ✅ OK |
| **Choix Amazon** | Non visible sur capture | Oui | ⚠️ À VÉRIFIER |
| **Référence (ASIN)** | B0CWLSQ8FS | B0CWLSQ8FS | ✅ OK |
| **Image principale** | Image RAM Crucial | Image RAM Crucial | ✅ OK |
## Défauts identifiés
### 1. ❌ ERREUR: MSRP incorrect (606,99 €)
**Problème**: Le Web UI affiche "PRIX CONSEILLÉ: 606,99€" alors qu'Amazon n'affiche aucun prix conseillé pour ce produit à 80€.
**Cause racine identifiée** :
Le prix 606,99€ provient de la section **"Découvrir et s'inspirer"** (`sims-discoveryAndInspiration_feature_div`), un produit similaire/recommandé.
```
Source HTML: #CardInstance5M3xhZWYVx9FloZeghy3og
Section parente: #sims-discoveryAndInspiration_feature_div_01
```
**Même problème que produit 1** : Le sélecteur MSRP (`span.a-text-price span.a-offscreen`) capture des prix barrés de produits tiers.
---
### 2. ❌ ERREUR: Stock "Inconnu" au lieu de "Expédié sous 5-6 jours"
**Problème**: Le stock est affiché comme "Inconnu" alors qu'Amazon indique clairement "Habituellement expédié sous 5 à 6 jours".
**Données HTML** :
```
#availability span: "Habituellement expédié sous 5 à 6 jours"
```
**Cause racine** :
Le parser ne reconnaît pas ce texte comme indicateur de stock. La méthode `_extract_stock_details()` cherche uniquement :
- "en stock" / "in stock" / "available" → IN_STOCK
- "rupture" / "indisponible" / "out of stock" → OUT_OF_STOCK
Le texte "expédié sous X jours" n'est pas mappé → retourne `UNKNOWN`.
**Fichier concerné**: [store.py:304-326](pricewatch/app/stores/amazon/store.py#L304-L326) - méthode `_extract_stock_details()`
**Correction proposée** :
```python
# Ajouter la reconnaissance des délais d'expédition
if "expédié sous" in normalized or "dispatched within" in normalized:
return StockStatus.IN_STOCK, text, True # Expédié = disponible
```
---
### 3. ⚠️ AVERTISSEMENT: Choix Amazon potentiellement incorrect
Le Web UI affiche "CHOIX AMAZON: Oui" mais le badge n'est pas clairement visible sur la capture Amazon.
**À vérifier** : Le badge peut être présent dans le HTML mais non visible sur la capture d'écran.
---
## Résumé produit 2
| Priorité | Problème | Récurrent ? |
|----------|----------|-------------|
| 🟠 P1 | MSRP incorrect (606,99€ d'un produit tiers) | ✅ Oui (même cause que produit 1) |
| 🟠 P1 | Stock "Inconnu" au lieu de "En stock (délai)" | 🆕 Nouveau pattern |
---
---
# Produit 1 : Corsair Vengeance LPX DDR4 32Go
**URL**: https://www.amazon.fr/dp/B07RW6Z692?th=1
---
## Comparaison: Données Amazon vs Données Scrapées
| Champ | Valeur Amazon (source) | Valeur Scrapée (Web UI) | Statut |
|-------|------------------------|-------------------------|--------|
| **Titre** | Corsair Vengeance LPX DDR4 RAM 32Go (2x16Go) 3200MHz CL16 Intel XMP 2.0 Mémoire d'ordinateur - Noir (CMK32GX4M2E3200C16) | Corsair Vengeance LPX DDR4 RAM 32Go (2x16Go) 3200MHz CL16 Intel XMP 2.0... | ⚠️ TRONQUÉ |
| **Prix** | 230,54 € | 230,54 € | ✅ OK |
| **Prix conseillé (MSRP)** | Non affiché sur Amazon | 139 € | ❌ ERREUR |
| **Stock** | "Plus de 500 achetés au cours du mois dernier" (implicitement en stock) | "En stock" | ✅ OK |
| **Note** | 4,8 | 4,8 | ✅ OK |
| **Nombre d'avis** | 28 245 | 28 245 | ✅ OK |
| **Choix Amazon** | Oui (badge visible) | Oui | ✅ OK |
| **Référence (ASIN)** | B07RW6Z692 | B07RW6Z692 | ✅ OK |
| **Image principale** | Image RAM Corsair Vengeance | Image processeur AMD Ryzen 7 | ❌ ERREUR CRITIQUE |
| **Catégorie** | Mémoire RAM | Mémoire RAM | ✅ OK |
---
## Défauts identifiés
### 1. ❌ ERREUR CRITIQUE: Mauvaise image produit
**Problème**: L'image affichée dans le Web UI est un processeur AMD Ryzen 7 5800X, alors que le produit est de la RAM Corsair Vengeance.
**Cause racine identifiée** :
L'image provient de la section **"Fréquemment achetés ensemble"** (`_p13n-desktop-sims-fbt`).
```
Image fautive: https://images-eu.ssl-images-amazon.com/images/I/61IIbwz-+ML._AC_UL116_SR116,116_.jpg
Contexte HTML: <a class="_p13n-desktop-sims-fbt_fbt-desktop_image-display__2oZhY asin-2">
```
**Données en base** :
```
ProductImage position=0 : 61IIbwz-+ML (Ryzen) ← INCORRECT
ProductImage position=1 : 61wCOVcyvFL (Corsair RAM) ← CORRECT
```
**Explication** :
- Le sélecteur `.a-dynamic-image` capture toutes les images dynamiques de la page
- L'image du Ryzen (dans "Fréquemment achetés ensemble") est trouvée AVANT l'image principale du produit
- Elle est donc insérée en position 0 (image principale)
**Fichier concerné**: [store.py:328-378](pricewatch/app/stores/amazon/store.py#L328-L378) - méthode `_extract_images()`
**Correction proposée** :
1. Prioriser `#landingImage` comme source unique pour l'image principale
2. Filtrer les images provenant de sections publicitaires (`_p13n-`, `sp_detail`, `sims-fbt`)
3. Ne pas utiliser `.a-dynamic-image` seul car trop large
---
### 2. ❌ ERREUR: Prix conseillé (MSRP) incorrect
**Problème**: Le Web UI affiche "PRIX CONSEILLÉ: 139€" alors qu'Amazon n'affiche aucun prix barré/conseillé pour ce produit.
**Cause racine identifiée** :
Le prix 139€ provient d'un **produit sponsorisé** affiché sur la page.
```
Source HTML: <div id="sp_detail2_B0FNMQQK58" class="sp_offerVertical p13n-asin sp_ltr_offer">
Texte: "Conseillé : 139,00 €"
```
**Données en base** :
```sql
Product.msrp = 139.00 Valeur stockée incorrecte
```
**Parser actuel** (test aujourd'hui) :
```python
snapshot.msrp = None Le parser actuel retourne correctement None
```
**Explication** :
- Un scraping précédent a capté ce prix sponsorisé comme MSRP
- Le sélecteur `span.a-text-price span.a-offscreen` est trop générique
- Il capture n'importe quel prix barré de la page (pubs, suggestions, etc.)
**Fichier concerné**: [store.py:276-285](pricewatch/app/stores/amazon/store.py#L276-L285) - méthode `_extract_msrp()`
**Correction proposée** :
1. Restreindre la recherche MSRP au conteneur principal du produit (`#corePrice_feature_div`, `#apex_desktop`)
2. Exclure explicitement les sections sponsorisées (`#sp_detail`, `.sp_offerVertical`)
3. Ajouter une validation : MSRP > prix actuel (sinon, ignorer)
---
### 3. ⚠️ AVERTISSEMENT: Titre tronqué
**Problème**: Le titre complet inclut "(CMK32GX4M2E3200C16)" qui semble coupé dans l'affichage.
**Données en base** :
```
Product.title = "Corsair Vengeance LPX DDR4 RAM 32Go (2x16Go) 3200MHz CL16 Intel XMP 2.0 Mémoire d'ordinateur - Noir (CMK32GX4M2E3200C16)"
```
**Verdict** : Le titre est complet en base. C'est un problème d'affichage UI (CSS truncation), pas de scraping.
---
### 4. ️ DONNÉES NON SCRAPÉES (opportunités)
| Donnée Amazon | Présence | Priorité |
|---------------|----------|----------|
| Prix en 4x (58,96€ x4 mois) | ❌ | Basse |
| "Frais d'importation Inclus" | ❌ | Moyenne |
| Marque: Corsair | ❌ (dans specs) | Basse |
| Specs techniques (DDR4, 32Go, 3200MHz) | ✅ (table specs) | - |
| "Plus de 500 achetés" | ❌ | Basse |
| Options de taille (variantes) | ❌ | Haute |
---
## Analyse technique approfondie
### État actuel du parser (test du 2026-01-18)
```python
# Résultat du parsing sur HTML frais
snapshot.title = "Corsair Vengeance LPX DDR4..."
snapshot.price = 230.54
snapshot.msrp = None (correct!)
snapshot.main_image = "61wCOVcyvFL" (image RAM correcte)
snapshot.rating_value = 4.8
snapshot.rating_count = 28246
snapshot.amazon_choice = True
```
**Conclusion** : Le parser actuel fonctionne correctement. Les erreurs proviennent de **données historiques** en base issues d'un scraping précédent bugué.
### Données corrompues en base
| Table | Champ | Valeur actuelle | Valeur attendue |
|-------|-------|-----------------|-----------------|
| `products` | `msrp` | 139.00 | NULL |
| `product_images` | position=0 | 61IIbwz-+ML (Ryzen) | 61wCOVcyvFL (RAM) |
---
## Résumé des priorités
| Priorité | Problème | Action requise |
|----------|----------|----------------|
| 🔴 P0 | Image principale incorrecte en BDD | Nettoyer les données + corriger l'extraction |
| 🟠 P1 | MSRP incorrect en BDD | Nettoyer les données + restreindre le sélecteur MSRP |
| 🟢 P2 | Parser actuel OK | Valider que les corrections persistent |
---
## Recommandations d'amélioration
### 1. Correction extraction images
```python
# Prioriser #landingImage uniquement pour l'image principale
landing = soup.select_one('#landingImage')
if landing:
main_image = landing.get('data-old-hires') or landing.get('src')
# Filtrer les images de sections publicitaires
EXCLUDED_PARENTS = ['_p13n-', 'sp_detail', 'sims-fbt', 'sponsored']
def _is_product_image(self, element) -> bool:
for parent in element.parents:
classes = ' '.join(parent.get('class', []))
if any(excl in classes for excl in EXCLUDED_PARENTS):
return False
return True
```
### 2. Correction extraction MSRP
```python
# Restreindre au conteneur principal
price_container = soup.select_one('#corePrice_feature_div, #apex_desktop')
if price_container:
msrp_element = price_container.select_one('span.a-text-price span.a-offscreen')
# Validation : MSRP doit être > prix actuel
if msrp and price and msrp <= price:
msrp = None # Ignorer les valeurs incohérentes
```
### 3. Migration données existantes
```sql
-- Corriger le MSRP incorrect
UPDATE products SET msrp = NULL WHERE reference = 'B07RW6Z692' AND msrp = 139.00;
-- Supprimer l'image Ryzen incorrecte
DELETE FROM product_images
WHERE product_id = (SELECT id FROM products WHERE reference = 'B07RW6Z692')
AND image_url LIKE '%61IIbwz%';
-- Réordonner les positions
UPDATE product_images SET position = position - 1
WHERE product_id = (SELECT id FROM products WHERE reference = 'B07RW6Z692')
AND position > 0;
```
---
## Fichiers à modifier
1. **[store.py](pricewatch/app/stores/amazon/store.py)** - Logique d'extraction Amazon
- `_extract_images()` : filtrer les sections publicitaires
- `_extract_msrp()` : restreindre au conteneur principal
2. **[selectors.yml](pricewatch/app/stores/amazon/selectors.yml)** - Ajouter sélecteurs exclusion
3. **Migration SQL** - Nettoyer les données corrompues existantes
---
## Prochaines étapes
1. ✅ Identifier les causes racines (fait)
2. ⬜ Implémenter les corrections dans le parser
3. ✅ Nettoyer les données corrompues en base → **Workaround actuel : suppression + ré-ajout du produit**
4. ⬜ Re-scraper le produit pour valider
5. ⬜ Tester sur d'autres produits Amazon
---
## Note importante
**Workaround temporaire** : La correction des données corrompues se fait actuellement par suppression puis ré-ajout du produit depuis le Web UI. Cela fonctionne car le parser actuel est correct.
**Limitation** : Cette méthode supprime l'historique des prix du produit. Une correction du parser reste nécessaire pour éviter de futures corruptions.
---
# Plan de corrections
## 1. Infrastructure : Installer Playwright
```bash
playwright install
```
**Impact** : Évite le fallback HTTP qui capture des MSRP incorrects.
---
## 1bis. Stratégie de fetch : Playwright prioritaire pour Amazon
### Constat
| Méthode | Avantage | Inconvénient pour Amazon |
|---------|----------|--------------------------|
| HTTP | Rapide | HTML contient plus de sections sponsorisées/recommandations |
| Playwright | HTML "propre" après JS | Plus lent |
### Flux actuel
```
HTTP d'abord → Si échec → Playwright (fallback)
```
### Flux proposé pour Amazon
```
Playwright d'abord → Si échec → HTTP (fallback)
```
### Implémentation
Ajouter une propriété `prefer_playwright` dans `BaseStore` :
```python
# pricewatch/app/stores/base.py
class BaseStore:
prefer_playwright: bool = False # Par défaut HTTP first
# pricewatch/app/stores/amazon/store.py
class AmazonStore(BaseStore):
prefer_playwright: bool = True # Playwright first pour Amazon
```
Modifier `scrape_product()` dans `tasks/scrape.py` :
```python
# Déterminer l'ordre de fetch selon le store
if store.prefer_playwright and use_playwright:
# Playwright d'abord
pw_result = fetch_playwright(...)
if pw_result.success:
html = pw_result.html
fetch_method = FetchMethod.PLAYWRIGHT
else:
# Fallback HTTP
http_result = fetch_http(...)
...
else:
# HTTP d'abord (comportement actuel)
...
```
---
## 1ter. Vérification du flux UI preview/commit
### Constat
Le flux **preview/commit existe déjà** et fonctionne correctement :
| Action UI | Endpoint | Persistence |
|-----------|----------|-------------|
| "Ajouter" → Preview | `/scrape/preview` | ❌ Non (save_db=False) |
| "Enregistrer" → Commit | `/scrape/commit` | ✅ Oui |
**Conclusion** : Pas de modification nécessaire côté UI. Les données sont bien persistées uniquement sur action explicite de l'utilisateur.
---
## 2. Parser : Restreindre extraction MSRP
**Fichier** : [store.py](pricewatch/app/stores/amazon/store.py)
### Problème identifié (Produit 8 - UGREEN)
Le parser utilise `select_one()` qui retourne le **premier** élément trouvé. Or Amazon affiche maintenant :
```
Prix actuel: 118,99€
Prix le plus bas des 30 derniers jours: 127,49€ ← PREMIER dans le DOM
Prix conseillé: 169,99€ (-30%) ← SECOND dans le DOM
```
**Résultat** : Le parser capture 127,49€ (prix bas 30j) au lieu de 169,99€ (MSRP).
### Analyse HTML réelle
```python
# Ordre des éléments span.a-text-price span.a-offscreen dans #corePriceDisplay_desktop_feature_div:
# 0: 127,49€ ← Prix le plus bas 30 jours (directive Omnibus UE)
# 1: 169,99€ ← Prix conseillé (MSRP réel)
```
### Structure HTML identifiée
Les deux prix sont dans des sections distinctes avec des classes CSS spécifiques :
```html
<!-- Prix le plus bas 30 jours (directive Omnibus UE) -->
<span class="basisPrice">
"Prix le plus bas des 30 derniers jours : "
<span class="a-price a-text-price" data-a-strike="true">
<span class="a-offscreen">127,49€</span>
</span>
</span>
<!-- Prix conseillé (MSRP) -->
<span class="srpPriceBlock">
"Prix conseillé : "
<span class="a-price a-text-price srpPriceBlockAUI">
<span class="a-offscreen">169,99€</span>
</span>
</span>
<span class="srpSavingsPercentageBlock">-30%</span>
```
### Classes identifiantes
| Élément | Classe CSS | Contenu |
|---------|------------|---------|
| Prix bas 30j | `basisPrice` | Prix le plus bas des 30 derniers jours |
| MSRP | `srpPriceBlock`, `srpPriceBlockAUI` | Prix conseillé |
### Correction proposée (méthode précise)
```python
def _extract_msrp(self, soup, debug):
"""Extrait le prix conseillé (MSRP) via les classes CSS spécifiques."""
# Méthode 1: Cibler directement srpPriceBlock (plus précis)
srp_block = soup.select_one('span.srpPriceBlock .a-offscreen, span.srpPriceBlockAUI .a-offscreen')
if srp_block:
return parse_price_text(srp_block.get_text(strip=True))
# Méthode 2: Fallback - chercher dans le conteneur prix en excluant basisPrice
price_container = soup.select_one('#corePriceDisplay_desktop_feature_div, #corePrice_feature_div, #apex_desktop')
if price_container:
for strike in price_container.select('span.a-text-price span.a-offscreen'):
# Exclure les prix dans basisPrice (prix bas 30j)
if not strike.find_parent(class_='basisPrice'):
return parse_price_text(strike.get_text(strip=True))
return None
```
### Avantages de cette approche
| Approche | Avantage | Inconvénient |
|----------|----------|--------------|
| `max()` des prix | Simple | Peut échouer si MSRP < prix bas 30j (rare) |
| Cibler `srpPriceBlock` | Précis, robuste | Dépend de la classe CSS Amazon |
| Exclure `basisPrice` | Fiable | Fallback si srpPriceBlock absent |
**Recommandation** : Combiner les deux méthodes (cibler srpPriceBlock en priorité, fallback avec exclusion basisPrice).
---
## 3. Parser : Reconnaître "expédié sous" comme stock disponible
**Fichier** : [store.py](pricewatch/app/stores/amazon/store.py)
```python
def _extract_stock_details(self, soup, debug):
# ... code existant ...
normalized = text.lower()
# Ajouter reconnaissance délai d'expédition
if "expédié sous" in normalized or "dispatched within" in normalized:
return StockStatus.IN_STOCK, text, True
# Ajouter reconnaissance bouton panier si #availability vide
if not text and soup.select_one('#add-to-cart-button'):
return StockStatus.IN_STOCK, "Disponible", True
```
---
## 4. Parser : Corriger détection Amazon Choice
**Fichier** : [store.py](pricewatch/app/stores/amazon/store.py)
```python
def _extract_amazon_choice(self, soup, debug):
element = soup.select_one('#acBadge_feature_div')
if element:
text = element.get_text(strip=True)
# Ne retourner True que si le texte contient vraiment le badge
if text and ("choix d'amazon" in text.lower() or "amazon's choice" in text.lower()):
return True, text
return None, None
```
---
## 5. UI : Ne pas afficher "Stock: Inconnu"
Si le stock n'est pas détecté, ne pas afficher la ligne plutôt que "Inconnu".
---
## 6. UI : Supprimer "EVOL. 30 JOURS"
Cette donnée est calculée, pas scrapée. Elle ne doit pas apparaître dans le bloc d'informations produit.
---
## 7. Classification : Améliorer règles de type
| Mot-clé actuel | Problème | Correction |
|----------------|----------|------------|
| "GeForce", "RTX" → Ecran | Carte graphique mal classée | Ajouter règle "Carte graphique" |
| "SODIMM" → PC portable | RAM mal classée | Prioriser "RAM" si DDR présent |
---
## Checklist de validation
- [ ] Playwright installé et fonctionnel
- [ ] MSRP = None pour produits sans prix barré sur Amazon
- [ ] MSRP > prix actuel (validation obligatoire)
- [ ] Stock "expédié sous X jours" → En stock
- [ ] Amazon Choice = False si élément vide
- [ ] UI ne montre pas "Stock: Inconnu"
- [ ] UI ne montre pas "EVOL. 30 JOURS"
- [ ] Classification correcte GPU / RAM
---
---
# SYNTHÈSE FINALE
## Produits analysés
| # | Produit | ASIN | Erreurs principales |
|---|---------|------|---------------------|
| 1 | Corsair Vengeance LPX DDR4 32Go | B07RW6Z692 | MSRP incorrect (139€), Image incorrecte (Ryzen) |
| 2 | Crucial Basics DDR4 8Go | B0CWLSQ8FS | MSRP incorrect (606,99€), Stock "Inconnu" |
| 3 | ASUS TUF Gaming A16 | B0DQ8M74KL | Amazon Choice faux positif |
| 4 | MSI RTX 5060 Ti | B0F32N1ZGH | MSRP incorrect (102,99€), Type "Ecran" |
| 5 | Samsung SSD 9100 Pro | B0DWFLPMM5 | Stock "Inconnu" (produit disponible) |
| 6 | Corsair Vengeance SODIMM 32Go | B08GSTF5NJ | MSRP incorrect (606,99€), Type "PC portable" |
| 7 | MSI Modern MD2412P Écran | B0CB4FDJT5 | ✅ OK (MSRP correct car affiché sur Amazon) |
| 8 | UGREEN Revodok Pro | B0FGHX59G2 | MSRP = prix bas 30j (127,49€), Amazon Choice faux positif, Type "Ecran" |
| 9 | Anker Prime Battery | B0BYNZXFM2 | MSRP < prix (39,99€ < 99,99€), Amazon Choice faux positif |
**Statistiques** : 8/9 produits avec erreurs, 1/9 correct
---
## Erreurs identifiées et solutions
### 🔴 ERREUR 1 : MSRP incorrect (prix de produits tiers)
**Fréquence** : 6/9 produits
**Symptômes** :
- MSRP provient de produits sponsorisés (`#sp_detail_*`)
- MSRP provient de recommandations (`#CardInstance*`, `#sims-*`)
- MSRP < prix actuel (absurde)
**Causes** :
1. Sélecteur `span.a-text-price span.a-offscreen` trop large (toute la page)
2. `select_one()` prend le 1er élément trouvé (peut être prix bas 30j)
3. Fallback HTTP expose plus de sections sponsorisées que Playwright
**Solutions** :
```python
def _extract_msrp(self, soup, debug, current_price):
# 1. Cibler directement la classe srpPriceBlock
srp = soup.select_one('span.srpPriceBlock .a-offscreen, span.srpPriceBlockAUI .a-offscreen')
if srp:
msrp = parse_price_text(srp.get_text(strip=True))
if msrp and current_price and msrp > current_price:
return msrp
# 2. Fallback: conteneur principal, exclure basisPrice
container = soup.select_one('#corePriceDisplay_desktop_feature_div, #corePrice_feature_div')
if container:
for strike in container.select('span.a-text-price span.a-offscreen'):
if not strike.find_parent(class_='basisPrice'):
msrp = parse_price_text(strike.get_text(strip=True))
if msrp and current_price and msrp > current_price:
return msrp
return None # Pas de MSRP valide
```
**Validation obligatoire** : `MSRP > prix actuel` (sinon ignorer)
---
### 🔴 ERREUR 2 : Amazon Choice faux positif
**Fréquence** : 3/9 produits
**Symptôme** : "CHOIX AMAZON: Oui" alors qu'aucun badge visible
**Cause** : L'élément `#acBadge_feature_div` existe dans le DOM mais est vide
**Solution** :
```python
def _extract_amazon_choice(self, soup, debug):
element = soup.select_one('#acBadge_feature_div')
if element:
text = element.get_text(strip=True)
# Vérifier que le texte contient vraiment le badge
if text and ("choix d'amazon" in text.lower() or "amazon's choice" in text.lower()):
return True, text
return None, None # Pas de badge
```
---
### 🟠 ERREUR 3 : Stock "Inconnu" affiché
**Fréquence** : 3/9 produits
**Symptômes** :
- "Habituellement expédié sous 5-6 jours" → Stock "Inconnu"
- Produit disponible (bouton panier) mais `#availability` vide → Stock "Inconnu"
**Causes** :
1. Parser ne reconnaît pas "expédié sous" comme indicateur de disponibilité
2. UI affiche "Inconnu" au lieu de masquer le champ
**Solutions** :
*Parser :*
```python
def _extract_stock_details(self, soup, debug):
# Reconnaître délai d'expédition
if "expédié sous" in normalized or "dispatched within" in normalized:
return StockStatus.IN_STOCK, text, True
# Détecter disponibilité via bouton panier
if not text and soup.select_one('#add-to-cart-button'):
return StockStatus.IN_STOCK, "Disponible", True
```
*UI :* Ne pas afficher "Stock: Inconnu" - masquer le champ si non détecté
---
### 🟠 ERREUR 4 : "EVOL. 30 JOURS" affiché
**Fréquence** : 9/9 produits (tous)
**Symptôme** : UI affiche "+0.0%" ou autre valeur calculée
**Cause** : Donnée calculée côté frontend, non scrapée
**Règle** : Seules les données présentes sur Amazon doivent être affichées
**Solution** : Supprimer la ligne "EVOL. 30 JOURS" du bloc d'informations produit
---
### 🟡 ERREUR 5 : Classification de type incorrecte
**Fréquence** : 3/9 produits
**Exemples** :
- Carte graphique RTX → "Ecran"
- RAM SODIMM → "PC portable"
- Docking station → "Ecran"
**Cause** : Règles de classification par mots-clés trop simplistes
**Solutions** :
| Mot-clé | Classification actuelle | Classification correcte |
|---------|------------------------|------------------------|
| RTX, GeForce, Radeon | Ecran | Carte graphique |
| DDR4, DDR5, SODIMM, DIMM | PC portable | RAM / Mémoire |
| DisplayPort, HDMI (seul) | Ecran | Vérifier contexte (docking ≠ écran) |
---
### 🟡 ERREUR 6 : Playwright non installé
**Symptôme** : Erreur `Executable doesn't exist at /root/.cache/ms-playwright/chromium_headless_shell-1200`
**Impact** : Fallback HTTP capture plus d'erreurs MSRP
**Solution** :
```bash
playwright install
```
---
## Priorités de correction
| Priorité | Correction | Impact | Effort |
|----------|------------|--------|--------|
| 🔴 P0 | Restreindre extraction MSRP + validation | 6 produits corrigés | Moyen |
| 🔴 P0 | Corriger détection Amazon Choice | 3 produits corrigés | Faible |
| 🟠 P1 | Installer Playwright | Réduit erreurs MSRP | Faible |
| 🟠 P1 | Améliorer détection stock | 3 produits corrigés | Faible |
| 🟠 P1 | Supprimer "EVOL. 30 JOURS" de l'UI | 9 produits corrigés | Faible |
| 🟡 P2 | Améliorer classification types | 3 produits corrigés | Moyen |
| 🟡 P2 | Masquer "Stock: Inconnu" dans l'UI | UX améliorée | Faible |
---
## Fichiers impactés par les corrections
### Backend - Parser Amazon
| Fichier | Fonction/Section | Modification | Erreur corrigée |
|---------|------------------|--------------|-----------------|
| [store.py](pricewatch/app/stores/amazon/store.py) | `_extract_msrp()` (L276-285) | Cibler `srpPriceBlock`, exclure `basisPrice`, valider MSRP > prix | ERREUR 1 |
| [store.py](pricewatch/app/stores/amazon/store.py) | `_extract_amazon_choice()` (L496-520) | Vérifier texte non vide avant retourner True | ERREUR 2 |
| [store.py](pricewatch/app/stores/amazon/store.py) | `_extract_stock_details()` (L304-326) | Reconnaître "expédié sous", détecter bouton panier | ERREUR 3 |
| [selectors.yml](pricewatch/app/stores/amazon/selectors.yml) | Section `msrp` | Ajouter `srpPriceBlock`, `srpPriceBlockAUI`, `basisPrice` | ERREUR 1 |
### Backend - Infrastructure
| Fichier | Fonction/Section | Modification | Erreur corrigée |
|---------|------------------|--------------|-----------------|
| [base.py](pricewatch/app/stores/base.py) | Classe `BaseStore` | Ajouter attribut `prefer_playwright: bool = False` | ERREUR 6 |
| [store.py](pricewatch/app/stores/amazon/store.py) | Classe `AmazonStore` | Définir `prefer_playwright = True` | ERREUR 6 |
| [scrape.py](pricewatch/app/tasks/scrape.py) | `scrape_product()` (L83-118) | Inverser ordre fetch si `store.prefer_playwright` | ERREUR 6 |
### Frontend - Web UI
| Fichier | Fonction/Section | Modification | Erreur corrigée |
|---------|------------------|--------------|-----------------|
| [App.vue](webui/src/App.vue) | Template carte produit | Supprimer ligne "EVOL. 30 JOURS" | ERREUR 4 |
| [App.vue](webui/src/App.vue) | Template carte produit | Masquer "Stock: Inconnu" (afficher seulement si stock connu) | ERREUR 3 |
### Base de données - Classification
| Fichier | Table/Section | Modification | Erreur corrigée |
|---------|---------------|--------------|-----------------|
| [repository.py](pricewatch/app/db/repository.py) | `apply_classification()` | Vérifier règles de classification | ERREUR 5 |
| Table `classification_rules` | Données | Ajouter règle "Carte graphique" (RTX, GeForce, Radeon) | ERREUR 5 |
| Table `classification_rules` | Données | Ajouter règle "RAM / Mémoire" (DDR4, DDR5, SODIMM, DIMM) | ERREUR 5 |
| Table `classification_rules` | Données | Modifier règle "Ecran" (exclure si contexte docking/carte graphique) | ERREUR 5 |
---
## Matrice de traçabilité Erreur → Fichiers
| Erreur | Fichiers impactés |
|--------|-------------------|
| **ERREUR 1** : MSRP incorrect | `store.py`, `selectors.yml` |
| **ERREUR 2** : Amazon Choice faux positif | `store.py` |
| **ERREUR 3** : Stock "Inconnu" | `store.py`, `App.vue` |
| **ERREUR 4** : "EVOL. 30 JOURS" | `App.vue` |
| **ERREUR 5** : Classification type | `repository.py`, table `classification_rules` |
| **ERREUR 6** : Playwright non installé | `base.py`, `store.py` (Amazon), `scrape.py` |
---
## Ordre de modification recommandé
1. **Installer Playwright** (prérequis)
```bash
playwright install
```
2. **Parser Amazon** (`store.py`)
- `_extract_msrp()` - correction principale
- `_extract_amazon_choice()` - correction simple
- `_extract_stock_details()` - amélioration
3. **Sélecteurs** (`selectors.yml`)
- Ajouter nouveaux sélecteurs MSRP
4. **Web UI** (`App.vue`)
- Supprimer "EVOL. 30 JOURS"
- Masquer "Stock: Inconnu"
5. **Classification** (BDD + `repository.py`)
- Ajouter règles manquantes
- Ajuster priorités
6. **Infrastructure fetch** (`base.py`, `scrape.py`)
- Optionnel : Playwright prioritaire pour Amazon
---
## Conclusion
L'analyse de 9 produits Amazon a révélé des erreurs systématiques principalement liées à :
1. **Extraction MSRP trop permissive** - Le parser capture des prix de produits tiers car le sélecteur n'est pas restreint au bloc prix principal
2. **Détection Amazon Choice sans validation** - Le parser retourne `True` si l'élément existe, même s'il est vide
3. **Affichage de données calculées** - L'UI affiche des données non présentes sur Amazon
Les corrections proposées sont relativement simples à implémenter et corrigeraient 8/9 produits testés. Le produit 7 (MSI Écran) était déjà correct car il possède un vrai MSRP affiché sur Amazon.
---
---
# PROPOSITION DE CORRECTION VALIDÉE
## 1. MSRP extraction trop permissive
**Problème** : Le sélecteur global `span.a-text-price span.a-offscreen` attrape des prix barrés dans des sections non-produit (sponsorisé, recommandations, "prix bas 30j").
**Solution détaillée (backend)** :
- Cibler d'abord la zone prix du produit principal : `#corePriceDisplay_desktop_feature_div`, `#corePrice_feature_div`, `#apex_desktop`
- Dans ce conteneur, prioriser `span.srpPriceBlock .a-offscreen` ou `span.srpPriceBlockAUI .a-offscreen`
- Exclure `basisPrice` (directive Omnibus "prix bas 30 jours")
- **Validation métier obligatoire** : ignorer MSRP si `msrp <= price` (ou si price absent)
**Cas limites** :
- MSRP présent mais prix courant absent (produit indisponible) → garder MSRP si cohérent, mais marquer debug
- MSRP en devise/format inattendu → garder `None` et tracer
**Fichiers** : `store.py`, `selectors.yml`
---
## 2. MSRP 1er match "prix bas 30j"
**Problème** : `select_one()` prend le premier prix barré, parfois le "prix bas 30j".
**Solution détaillée** :
- Si `srpPriceBlock` absent, parcourir tous les `span.a-text-price span.a-offscreen` du conteneur prix
- Exclure ceux dont un parent a la classe `basisPrice`
- Si plusieurs candidats, prendre le plus élevé uniquement si price est présent et `msrp > price`
**Fichier** : `store.py`
---
## 3. Amazon Choice faux positif
**Problème** : `#acBadge_feature_div` existe mais vide → renvoyé `True`.
**Solution détaillée** :
- Vérifier texte non vide + correspondance stricte ("choix d'amazon" ou "amazon's choice")
- Si vide → `False` + conserver debug avec le DOM trouvé
**Fichier** : `store.py`
---
## 4. Stock "expédié sous X jours"
**Problème** : Texte d'expédition n'est pas reconnu comme dispo.
**Solution détaillée** :
- Normaliser le texte (`lower()`, `strip()`)
- Pattern matching :
- `expédié sous`, `habituellement expédié sous`, `dispatched within` → `IN_STOCK`
- Si `#availability` vide mais `#add-to-cart-button` présent → `IN_STOCK` ("Disponible")
- **UI** : si le statut reste `UNKNOWN`, ne rien afficher
**Fichiers** : `store.py`, `App.vue`
---
## 5. Images mauvaise image principale
**Problème** : `.a-dynamic-image` agrège images de sections tiers (sims, fbt, ads).
**Solution détaillée** :
- Prioriser `#landingImage` (ou `img#imgTagWrapperId img`)
- Filtrer toute image dont un parent contient `_p13n-`, `sims-fbt`, `sp_detail`, `sponsored`
- Garder un fallback seulement si `landingImage` absent
**Fichier** : `store.py`
---
## 6. UI "EVOL. 30 JOURS"
**Problème** : Info calculée non présente sur Amazon.
**Solution détaillée** : Retirer ce champ du bloc produit et garder l'historique dans le graphique si besoin.
**Fichier** : `App.vue`
---
## 7. Fetch Amazon Playwright prioritaire
**Problème** : Fallback HTTP expose plus de sections sponsorisées.
**Solution détaillée** :
- Attribut `prefer_playwright` dans `BaseStore`
- Inverser l'ordre dans `scrape_product()` si store le demande
**Risque** : Temps de scraping plus long, besoin de navigateur installé.
**Fichiers** : `base.py`, `store.py`, `scrape.py`
---
## 8. Classification type incorrect
**Problème** : Règles mots-clés trop générales.
**Solution détaillée** :
- Règles prioritaires :
- **GPU** : `RTX|GeForce|Radeon|GPU|Carte graphique`
- **RAM** : `DDR4|DDR5|SODIMM|DIMM|mémoire`
- **Docking** : `dock|hub|station` → ne pas classer en "Ecran"
- Appliquer priorité stricte (GPU > écran, RAM > PC portable)
**Fichiers** : `repository.py` et/ou table `classification_rules`
---
## 9. Données corrompues existantes
**Problème** : MSRP et image déjà stockés incorrectement.
**Solution détaillée** :
- Migration ou script de correction ciblée (par ASIN)
- Alternative : purge + re-scrape (perte historique)
**Fichiers** : SQL ou script dédié (à définir)
---
## Plan d'implémentation recommandé
### Phase 1 : Corrections critiques (P0)
| Étape | Action | Fichier | Test |
|-------|--------|---------|------|
| 1.1 | Installer Playwright | Terminal | `playwright install` |
| 1.2 | Corriger `_extract_msrp()` | `store.py` | Produits 1,2,4,6,8,9 |
| 1.3 | Corriger `_extract_amazon_choice()` | `store.py` | Produits 3,8,9 |
| 1.4 | Ajouter sélecteurs MSRP | `selectors.yml` | - |
### Phase 2 : Corrections importantes (P1)
| Étape | Action | Fichier | Test |
|-------|--------|---------|------|
| 2.1 | Corriger `_extract_stock_details()` | `store.py` | Produits 2,5 |
| 2.2 | Corriger `_extract_images()` | `store.py` | Produit 1 |
| 2.3 | Supprimer "EVOL. 30 JOURS" | `App.vue` | Tous produits |
| 2.4 | Masquer "Stock: Inconnu" | `App.vue` | Produits 2,5 |
### Phase 3 : Améliorations (P2)
| Étape | Action | Fichier | Test |
|-------|--------|---------|------|
| 3.1 | Ajouter règles classification | BDD | Produits 4,6,8 |
| 3.2 | Ajouter `prefer_playwright` | `base.py`, `scrape.py` | Amazon |
| 3.3 | Script correction données | SQL | Produits existants |
---
## Validation finale
Après implémentation, re-tester les 9 produits :
| # | ASIN | Erreurs avant | Attendu après |
|---|------|---------------|---------------|
| 1 | B07RW6Z692 | MSRP, Image | ✅ OK |
| 2 | B0CWLSQ8FS | MSRP, Stock | ✅ OK |
| 3 | B0DQ8M74KL | Amazon Choice | ✅ OK |
| 4 | B0F32N1ZGH | MSRP, Type | ✅ OK |
| 5 | B0DWFLPMM5 | Stock | ✅ OK |
| 6 | B08GSTF5NJ | MSRP, Type | ✅ OK |
| 7 | B0CB4FDJT5 | ✅ OK | ✅ OK |
| 8 | B0FGHX59G2 | MSRP, Amazon Choice, Type | ✅ OK |
| 9 | B0BYNZXFM2 | MSRP, Amazon Choice | ✅ OK |