116 lines
3.6 KiB
Markdown
Executable File
116 lines
3.6 KiB
Markdown
Executable File
# Fixtures Cdiscount
|
|
|
|
Ce dossier contient des fichiers HTML réels capturés depuis Cdiscount.com pour les tests.
|
|
|
|
## ⚠️ Note importante sur Cdiscount
|
|
|
|
Cdiscount utilise une **protection anti-bot forte** (Cloudflare/Baleen):
|
|
- HTTP simple retourne une page de protection JavaScript (~14 KB)
|
|
- **Playwright est OBLIGATOIRE** pour récupérer le vrai contenu
|
|
- Temps de chargement: ~2-3 secondes
|
|
|
|
## Fichiers
|
|
|
|
### cdiscount_tuf608umrv004_pw.html
|
|
- **Produit**: PC Portable Gamer ASUS TUF Gaming A16
|
|
- **SKU**: tuf608umrv004
|
|
- **URL**: https://www.cdiscount.com/informatique/ordinateurs-pc-portables/pc-portable-gamer-asus-tuf-gaming-a16-sans-windo/f-10709-tuf608umrv004.html
|
|
- **Taille**: ~310 KB
|
|
- **Lignes**: 399
|
|
- **Méthode**: Playwright (headless)
|
|
- **Date capture**: 2026-01-13
|
|
- **Usage**: Test complet parsing produit tech
|
|
|
|
## Différences avec Amazon
|
|
|
|
| Aspect | Amazon | Cdiscount |
|
|
|--------|--------|-----------|
|
|
| **Anti-bot** | Faible (HTTP OK) | ✗ Fort (Playwright requis) |
|
|
| **Sélecteurs** | Stables (IDs) | Instables (classes générées) |
|
|
| **Structure** | `#productTitle`, `.a-price` | `data-e2e="title"`, classes dynamiques |
|
|
| **Prix** | 3 parties (whole+fraction+symbol) | Texte direct "1499,99 €" |
|
|
| **Référence** | ASIN dans URL `/dp/{ASIN}` | SKU dans URL `/f-{cat}-{SKU}.html` |
|
|
| **Catégorie** | Breadcrumb HTML | Dans l'URL path |
|
|
| **Specs** | Tables HTML | Peut être dans onglets cachés |
|
|
|
|
## Sélecteurs identifiés
|
|
|
|
### Titre
|
|
```css
|
|
h1[data-e2e="title"]
|
|
```
|
|
Exemple: "PC Portable Gamer ASUS TUF Gaming A16 | Sans Windows - 16" WUXGA..."
|
|
|
|
### Prix
|
|
Classes CSS instables. Utiliser **regex sur le texte**:
|
|
```regex
|
|
(\d+[,\.]\d+)\s*€
|
|
```
|
|
Exemple: "1199,99 €" → 1199.99
|
|
|
|
### Images
|
|
```css
|
|
img[alt]
|
|
```
|
|
Filtrer celles dont `alt` contient le titre du produit.
|
|
Format URL: `https://www.cdiscount.com/pdt2/0/0/4/{num}/700x700/{sku}/rw/...`
|
|
|
|
### SKU
|
|
Extraction depuis l'URL:
|
|
```regex
|
|
/f-\d+-([a-z0-9]+)\.html
|
|
```
|
|
Groupe 1 = SKU (ex: `tuf608umrv004`)
|
|
|
|
### Catégorie
|
|
Extraction depuis l'URL path:
|
|
```
|
|
/informatique/ordinateurs-pc-portables/...
|
|
^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
|
|
catégorie1 catégorie2
|
|
```
|
|
|
|
## Utilisation dans les tests
|
|
|
|
```python
|
|
@pytest.fixture
|
|
def cdiscount_fixture_tuf608umrv004():
|
|
fixture_path = Path(__file__).parent.parent.parent / \
|
|
"pricewatch/app/stores/cdiscount/fixtures/cdiscount_tuf608umrv004_pw.html"
|
|
with open(fixture_path, "r", encoding="utf-8") as f:
|
|
return f.read()
|
|
|
|
def test_parse_real_fixture(store, cdiscount_fixture_tuf608umrv004):
|
|
url = "https://www.cdiscount.com/informatique/.../f-10709-tuf608umrv004.html"
|
|
snapshot = store.parse(cdiscount_fixture_tuf608umrv004, url)
|
|
|
|
assert snapshot.title is not None
|
|
assert "ASUS" in snapshot.title
|
|
assert snapshot.price == 1199.99
|
|
assert snapshot.reference == "tuf608umrv004"
|
|
```
|
|
|
|
## Points d'attention pour les tests
|
|
|
|
1. **Ne pas tester les valeurs exactes** (prix, nombre d'avis) car elles changent
|
|
2. **Tester le format** et la présence des données
|
|
3. **Prévoir des fallbacks** pour chaque champ (sélecteurs instables)
|
|
4. Les classes CSS peuvent changer à tout moment
|
|
5. Utiliser `data-e2e` attributes quand disponibles (plus stables)
|
|
6. Parser le prix par regex plutôt que par sélecteurs CSS
|
|
|
|
## Comment capturer une nouvelle fixture
|
|
|
|
```python
|
|
from pricewatch.app.scraping.pw_fetch import fetch_playwright
|
|
|
|
url = "https://www.cdiscount.com/..."
|
|
result = fetch_playwright(url, headless=True, timeout_ms=60000)
|
|
|
|
if result.success:
|
|
with open("fixture.html", "w", encoding="utf-8") as f:
|
|
f.write(result.html)
|
|
```
|
|
|
|
⚠️ **N'utilisez JAMAIS** `fetch_http()` pour Cdiscount - cela ne fonctionnera pas!
|