Amazon modifie fréquemment les mises en page des résultats de recherche, les emplacements publicitaires et les structures des fiches produits. Les scrapers tombent en panne lorsque cela se produit. Ce tutoriel surveille la structure des SERP Amazon via l'API Scavio, détectant quand les formats de résultats changent, de nouveaux éléments apparaissent ou les positions des produits se déplacent. Chaque vérification coûte 0,005 $.
Prérequis
- Python 3.8+
- bibliothèque requests
- Une clé API Scavio depuis scavio.dev
- Catégories de produits Amazon à surveiller
Parcours
Étape 1: Capturer des instantanés de la structure SERP Amazon
Enregistrer la structure des résultats de recherche Amazon, y compris les types et positions des résultats.
import os, requests, json, hashlib
from datetime import datetime
API_KEY = os.environ['SCAVIO_API_KEY']
SH = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}
def snapshot_amazon_serp(query):
data = requests.post('https://api.scavio.dev/api/v1/search',
headers=SH, json={'query': query, 'platform': 'amazon', 'country_code': 'us'}, timeout=10).json()
organic = data.get('organic_results', [])
structure = {
'query': query,
'timestamp': datetime.now().isoformat(),
'total_results': len(organic),
'result_types': [],
'has_sponsored': any('sponsored' in json.dumps(r).lower() for r in organic),
'has_ratings': sum(1 for r in organic if r.get('rating')),
'has_prices': sum(1 for r in organic if r.get('price') or r.get('extracted_price')),
'top_domains': list(set(r.get('displayed_link', '').split('/')[0] for r in organic[:5])),
}
for r in organic:
rtype = 'sponsored' if 'sponsored' in json.dumps(r).lower() else 'organic'
structure['result_types'].append(rtype)
content_hash = hashlib.md5(json.dumps(structure['result_types']).encode()).hexdigest()
structure['layout_hash'] = content_hash
return structure
QUERIES = ['wireless earbuds', 'protein powder', 'usb c hub']
for q in QUERIES:
snap = snapshot_amazon_serp(q)
sponsored = snap['result_types'].count('sponsored')
organic = snap['result_types'].count('organic')
print(f' {q:30} | Results: {snap["total_results"]} | Organic: {organic} | Sponsored: {sponsored} | Hash: {snap["layout_hash"][:8]}')
print(f'\nCost: ${len(QUERIES) * 0.005:.3f}')Étape 2: Comparer les instantanés pour détecter les changements de mise en page
Différencier les instantanés quotidiens pour trouver les changements structurels dans les résultats Amazon.
HISTORY_FILE = 'amazon_layout_history.json'
def save_and_compare(snapshots):
try:
with open(HISTORY_FILE) as f:
history = json.load(f)
except FileNotFoundError:
history = []
today = {'date': datetime.now().strftime('%Y-%m-%d'), 'snapshots': snapshots}
changes = []
if history:
prev_snaps = {s['query']: s for s in history[-1]['snapshots']}
for snap in snapshots:
prev = prev_snaps.get(snap['query'])
if not prev:
continue
if snap['layout_hash'] != prev['layout_hash']:
changes.append({'query': snap['query'], 'type': 'layout_change',
'detail': f'Layout hash changed: {prev["layout_hash"][:8]} -> {snap["layout_hash"][:8]}'})
if snap['total_results'] != prev['total_results']:
changes.append({'query': snap['query'], 'type': 'result_count',
'detail': f'Results: {prev["total_results"]} -> {snap["total_results"]}'})
prev_sponsored = prev['result_types'].count('sponsored')
curr_sponsored = snap['result_types'].count('sponsored')
if prev_sponsored != curr_sponsored:
changes.append({'query': snap['query'], 'type': 'ad_change',
'detail': f'Sponsored: {prev_sponsored} -> {curr_sponsored}'})
history.append(today)
with open(HISTORY_FILE, 'w') as f:
json.dump(history, f, indent=2)
return changes
snapshots = [snapshot_amazon_serp(q) for q in QUERIES]
changes = save_and_compare(snapshots)
print(f'\nLayout changes detected: {len(changes)}')
for c in changes:
print(f' [{c["type"]:15}] {c["query"]:25} | {c["detail"]}')Étape 3: Générer un rapport de changement de mise en page
Compiler les changements en un rapport exploitable pour les équipes e-commerce.
def layout_change_report(changes, snapshots):
print(f'\n{"=" * 60}')
print(f' Amazon Layout Change Report - {datetime.now().strftime("%Y-%m-%d")}')
print(f'{"=" * 60}')
print(f'\n Queries monitored: {len(snapshots)}')
print(f' Changes detected: {len(changes)}')
# Current structure summary
print(f'\n Current Layout Summary:')
for snap in snapshots:
sponsored = snap['result_types'].count('sponsored')
organic = snap['result_types'].count('organic')
print(f' {snap["query"]:25} | Organic: {organic} | Sponsored: {sponsored} | Prices: {snap["has_prices"]} | Ratings: {snap["has_ratings"]}')
if changes:
by_type = {}
for c in changes:
by_type.setdefault(c['type'], []).append(c)
for ctype, items in by_type.items():
print(f'\n {ctype.replace("_", " ").title()} ({len(items)}):')
for item in items:
print(f' {item["query"]}: {item["detail"]}')
else:
print(f'\n No layout changes since last scan.')
print(f'\n Daily cost: ${len(snapshots) * 0.005:.3f}')
print(f' No scrapers needed. No proxy rotation. No HTML parsing.')
layout_change_report(changes, snapshots)Exemple Python
import os, requests, json, hashlib
SH = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}
def amazon_layout(query):
data = requests.post('https://api.scavio.dev/api/v1/search',
headers=SH, json={'query': query, 'platform': 'amazon', 'country_code': 'us'}, timeout=10).json()
results = data.get('organic_results', [])
h = hashlib.md5(json.dumps([r.get('position') for r in results]).encode()).hexdigest()[:8]
print(f'{query[:30]:30} | {len(results)} results | hash: {h}')
amazon_layout('wireless earbuds')Exemple JavaScript
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
const data = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST', headers: SH,
body: JSON.stringify({ query: 'wireless earbuds', platform: 'amazon', country_code: 'us' })
}).then(r => r.json());
console.log(`Results: ${(data.organic_results || []).length}`);Sortie attendue
wireless earbuds | Results: 10 | Organic: 7 | Sponsored: 3 | Hash: 4f2a8b1c
protein powder | Results: 10 | Organic: 8 | Sponsored: 2 | Hash: 7d3e9f5a
usb c hub | Results: 10 | Organic: 8 | Sponsored: 2 | Hash: 1b6c4d8e
Cost: $0.015
Layout changes detected: 1
[ad_change ] wireless earbuds | Sponsored: 2 -> 3
============================================================
Amazon Layout Change Report - 2026-05-21
============================================================
Queries monitored: 3
Changes detected: 1
No scrapers needed. No proxy rotation. No HTML parsing.