Monitoraggio Posizionamento Keyword Amazon con Proxy: Guida per Sviluppatori

Una guida pratica in Python per monitorare il posizionamento organico dei prodotti su Amazon usando proxy residenziali, parsing del SERP e best practice di produzione.

Amazon Keyword Rank Tracking with Proxies: A Developer's Guide

Nota legale: raccogli solo dati di ricerca pubblici e monitora i tuoi listing o quelli per cui hai autorizzazione. Negli Stati Uniti, accessi non autorizzati possono ricadere sotto il Computer Fraud and Abuse Act (CFAA); nell'UE, il trattamento di dati personali è disciplinato dal GDPR. Rispetta i ToS di Amazon, il file robots.txt e considera l'Amazon SP-API quando disponibile. Questo articolo è a scopo educativo.

Il monitoraggio posizionamento keyword Amazon con proxy è la base di ogni strategia di marketplace SEO. A differenza di Google, Amazon è contemporaneamente motore di ricerca e storefront: l'algoritmo (A9) premia rilevanza testuale e sales velocity, non solo autorità del dominio. Per seller, agenzie e sviluppatori, tracciare nel tempo la posizione organica di un ASIN per keyword e marketplace è l'unico modo per misurare l'impatto di ottimizzazioni listing, PPC e variazioni di prezzo.

Perché Amazon è un SERP a sé stante

Il amazon serp scraping richiede un approccio diverso dal classico web scraping. Il SERP di Amazon combina risultati organici, sponsorizzati (Sponsored Products), Brand Stores e contenuti editoriali in un'unica griglia. La posizione organica di un ASIN dipende da:

  • Rilevanza: match tra query, titolo, bullet point, backend keywords e categoria.
  • Sales velocity storica e corrente: conversioni recenti pesano molto.
  • Disponibilità e prezzo: out-of-stock e prezzo non competitivo declassano rapidamente.
  • Recensioni e rating: volume e media influenzano ranking e CTR.

Questo significa che un ASIN può essere #3 su "cuffie bluetooth" su amazon.it e #12 su amazon.de per la stessa keyword tradotta. Per confrontare apples-to-apples devi fissare marketplace, keyword e ASIN. Senza un amazon keyword rank tracker automatizzato, la copertura su più keyword e marketplace è manualmente impraticabile.

Architettura del flusso di tracking

Un sistema di tracking ha quattro componenti:

  1. Fetcher: client HTTP (o browser headless) che richiama https://www.amazon.{tld}/s?k={keyword}&page={n} tramite proxy.
  2. Parser estrae ASIN e posizione dai nodi [data-component-type="s-search-result"].
  3. Matcher confronta l'ASIN target con i risultati e registra la posizione organica.
  4. Storage: SQLite/Postgres con schema (date, marketplace, keyword, asin, organic_position, sponsored_position).

Il punto critico è il fetcher: Amazon applica anti-bot aggressivo (rate limiting, CAPTCHA, soft-block con redirect a /errors/validateCaptcha) e localizza i risultati per marketplace. Ecco perché i proxy residenziali con geo-targeting sono obbligatori.

Perché servono proxy residenziali con geo-targeting

Amazon decide lingua, valuta e risultati in base a IP, cookie e header Accept-Language. Un datacenter IP tedesco su amazon.com può comunque ricevere risultati localizzati o essere sfidato da CAPTCHA. I proxy residenziali usano IP assegnati a ISP reali, quindi appaiono come traffico consumer legittimo. Con ProxyHat puoi forzare il paese nel username:

  • user-country-US per amazon.com (USA).
  • user-country-DE per amazon.de (Germania).
  • user-country-IT per amazon.it.

Per paginare i risultati in modo stabile (page 1→5) serve una sessione sticky: lo stesso IP per tutta la keyword, altrimenti Amazon può mostrare risultati inconsistenti tra pagine. ProxyHat supporta -session-{id} nel username.

Setup ProxyHat: formato credenziali

Endpoint HTTP: http://USERNAME:PASSWORD@gate.proxyhat.com:8080. Le flag di geo e sessione vanno nel username.

# HTTP — USA, amazon.com, sessione sticky
http://user-country-US-session-kw1abc:pass@gate.proxyhat.com:8080

# HTTP — Germania, amazon.de
http://user-country-DE-session-kw2def:pass@gate.proxyhat.com:8080

# SOCKS5 (quando serve TLS fingerprint diverso)
socks5://user-country-IT-session-kw3ghi:pass@gate.proxyhat.com:1080

Consulta la pagina locations per la lista dei paesi supportati e pricing per i piani.

Esempio 1: fetch grezzo con curl_cffi

curl_cffi imita il TLS fingerprint di Chrome, utile contro fingerprinting base. Ecco un fetcher minimale con retry e backoff.

# pip install curl_cffi tenacity
from curl_cffi import requests
from tenacity import retry, stop_after_attempt, wait_exponential
import urllib.parse
import time

PROXY = "http://user-country-US-session-kw1abc:pass@gate.proxyhat.com:8080"

def build_url(keyword: str, page: int, tld: str = "com") -> str:
    q = urllib.parse.quote(keyword)
    return f"https://www.amazon.{tld}/s?k={q}&page={page}"

@retry(stop=stop_after_attempt(4), wait=wait_exponential(multiplier=2, min=2, max=30))
def fetch_page(keyword: str, page: int, tld: str = "com") -> str:
    url = build_url(keyword, page, tld)
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                     "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
        "Accept-Language": "en-US,en;q=0.9",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    }
    r = requests.get(url, headers=headers, proxies={"http": PROXY, "https": PROXY},
                     impersonate="chrome124", timeout=30)
    if r.status_code != 200 or "validateCaptcha" in r.url:
        raise RuntimeError(f"blocked or captcha: {r.status_code} {r.url}")
    time.sleep(1.5)  # throttle
    return r.text

html = fetch_page("wireless earbuds", 1, "com")
print(len(html))

Esempio 2: parsing del SERP

Il selettore chiave è [data-component-type="s-search-result"]. Ogni card ha data-asin. I prodotti sponsorizzati contengono un'etichetta "Sponsored" (testo o attributo aria-label).

# pip install lxml selectolax
from selectolax.parser import HTMLParser
from dataclasses import dataclass

@dataclass
class SerpItem:
    asin: str
    organic_position: int
    is_sponsored: bool
    page: int

def parse_serp(html: str, page: int) -> list[SerpItem]:
    tree = HTMLParser(html)
    cards = tree.css('[data-component-type="s-search-result"]')
    items = []
    organic_idx = 0
    for card in cards:
        asin = card.attributes.get("data-asin", "")
        if not asin:
            continue
        # detect Sponsored label
        sponsored_node = card.css_first('span.a-color-secondary')
        is_sponsored = False
        if sponsored_node and "Sponsored" in (sponsored_node.text() or ""):
            is_sponsored = True
        if not is_sponsored:
            organic_idx += 1
            pos = organic_idx
        else:
            pos = -1  # non organico
        items.append(SerpItem(asin=asin, organic_position=pos,
                             is_sponsored=is_sponsored, page=page))
    return items

items = parse_serp(html, 1)
for it in items[:5]:
    print(it)

Nota: la struttura HTML di Amazon cambia spesso. Verifica i selettori con un dump locale prima di andare in produzione e aggiungi fallback (es. ricerca testo "Sponsored" in tutto il card).

Esempio 3: tracker completo con storico

Combina fetch + parse + match ASIN + storage SQLite. Usa una sessione sticky per tutta la keyword.

# pip install curl_cffi tenacity selectolax
import sqlite3
import time
import urllib.parse
from curl_cffi import requests
from tenacity import retry, stop_after_attempt, wait_exponential
from selectolax.parser import HTMLParser
from datetime import date

DB = "rank.db"

def init_db():
    con = sqlite3.connect(DB)
    con.execute("""CREATE TABLE IF NOT EXISTS ranks(
        d DATE, marketplace TEXT, keyword TEXT, asin TEXT,
        organic_pos INT, sponsored_pos INT, page INT,
        PRIMARY KEY(d, marketplace, keyword, asin))""")
    con.commit(); con.close()

PROXY_TMPL = "http://user-country-{cc}-session-{sid}:pass@gate.proxyhat.com:8080"

@retry(stop=stop_after_attempt(4), wait=wait_exponential(multiplier=2, min=2, max=30))
def fetch(keyword, page, tld, cc, sid):
    q = urllib.parse.quote(keyword)
    url = f"https://www.amazon.{tld}/s?k={q}&page={page}"
    proxy = PROXY_TMPL.format(cc=cc, sid=sid)
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                     "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
        "Accept-Language": "en-US,en;q=0.9",
    }
    r = requests.get(url, headers=headers, proxies={"http": proxy, "https": proxy},
                     impersonate="chrome124", timeout=30)
    if r.status_code != 200 or "validateCaptcha" in r.url:
        raise RuntimeError(f"blocked: {r.status_code}")
    time.sleep(1.2)
    return r.text

def parse(html, page):
    tree = HTMLParser(html)
    out = []
    oidx = 0
    for card in tree.css('[data-component-type="s-search-result"]'):
        asin = card.attributes.get("data-asin", "")
        if not asin:
            continue
        sp = card.css_first('span.a-color-secondary')
        is_sp = bool(sp and "Sponsored" in (sp.text() or ""))
        if not is_sp:
            oidx += 1
            out.append((asin, oidx, -1, page))
        else:
            out.append((asin, -1, page, page))  # sponsored_pos approssimato
    return out

def track(keyword, target_asin, tld, cc, max_pages=5):
    sid = f"{keyword[:4]}{target_asin[:4]}"
    today = date.today().isoformat()
    con = sqlite3.connect(DB)
    found = None
    for page in range(1, max_pages + 1):
        try:
            html = fetch(keyword, page, tld, cc, sid)
        except Exception as e:
            print(f"page {page} failed: {e}")
            break
        for asin, opos, spos, pg in parse(html, page):
            if asin == target_asin and opos != -1:
                found = opos
            con.execute("INSERT OR REPLACE INTO ranks VALUES(?,?,?,?,?,?,?)",
                        (today, tld, keyword, asin, opos, spos, pg))
        con.commit()
        if found:
            break
    con.close()
    return found

init_db()
pos = track("wireless earbuds", "B0CX22VV5R", "com", "US", max_pages=5)
print(f"organic position: {pos}")

Esempio 4: Playwright per SERP pesanti

Quando Amazon serve risultati via JS o serve un challenge, un browser headless è più robusto. Playwright supporta proxy con autenticazione.

# pip install playwright && playwright install chromium
from playwright.sync_api import sync_playwright

def track_pw(keyword, target_asin, tld="com", cc="US", max_pages=3):
    sid = f"pw{keyword[:4]}"
    proxy_user = f"user-country-{cc}-session-{sid}"
    with sync_playwright() as p:
        browser = p.chromium.launch(
            proxy={"server": "http://gate.proxyhat.com:8080",
                   "username": proxy_user, "password": "pass"},
            headless=True)
        ctx = browser.new_context(user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
            locale="en-US", extra_http_headers={"Accept-Language": "en-US,en;q=0.9"})
        page = ctx.new_page()
        import urllib.parse, time
        found = None
        for n in range(1, max_pages + 1):
            url = f"https://www.amazon.{tld}/s?k={urllib.parse.quote(keyword)}&page={n}"
            page.goto(url, timeout=45000, wait_until="domcontentloaded")
            if "validateCaptcha" in page.url:
                print("captcha at page", n); break
            cards = page.query_selector_all('[data-component-type="s-search-result"]')
            oidx = 0
            for c in cards:
                asin = c.get_attribute("data-asin")
                if not asin: continue
                txt = c.inner_text()
                is_sp = "Sponsored" in txt
                if not is_sp:
                    oidx += 1
                    if asin == target_asin:
                        found = oidx
                        break
            if found: break
            time.sleep(1.5)
        browser.close()
    return found

print(track_pw("wireless earbuds", "B0CX22VV5R"))

Esempio 5: scheduler giornaliero con circuituito

# pip install apscheduler
from apscheduler.schedulers.blocking import BlockingScheduler
import logging
logging.basicConfig(level=logging.INFO)

JOBS = [
    ("wireless earbuds", "B0CX22VV5R", "com", "US"),
    ("kabellose kopfhörer", "B0CX22VV5R", "de", "DE"),
]

def run_all():
    for kw, asin, tld, cc in JOBS:
        try:
            pos = track(kw, asin, tld, cc, max_pages=5)
            logging.info(f"{kw} @ {tld} -> pos {pos}")
        except Exception as e:
            logging.error(f"{kw} failed: {e}")

sched = BlockingScheduler()
sched.add_job(run_all, "cron", hour=7, minute=30)
run_all()  # test
sched.start()

Production tips

  • Throttle: 1–2 richieste/sec per sessione, pausa 1–2s tra le pagine. Più concorrenza = più IP distinti (una sessione per keyword).
  • Retry con backoff: usa tenacity con wait esponenziale 2–30s e max 4 tentativi. Distingui 503 (retry) da 200+CAPTCHA (cambia IP/sessione).
  • CAPTCHA detection: controlla "validateCaptcha" in r.url o la presenza di form action="/errors/validateCaptcha". Al CAPTCHA, ruota sessione e riprova dopo 60s.
  • Indexation check: prima di tracciare la posizione, verifica che l'ASIN sia indicizzato per quella keyword: se dopo 5 pagine non compare, registra NULL (non 0) per distinguerlo da "non trovato in page 1".
  • Soft 200: a volte Amazon risponde 200 con una pagina vuota o di errore. Verifica la presenza di almeno N card (len(cards) > 5).
  • Header realistici: mantieni Accept-Language coerente con il marketplace (es. de-DE,de;q=0.9 per amazon.de).
  • Monitoring: logga tasso di successo per sessione; se scende sotto il 90%, riduci la concorrenza o cambia pool.

Confronto: curl_cffi vs Playwright vs SP-API

ApproccioVelocitàRobustezza anti-botCostoQuando usarlo
curl_cffi + proxy residenziali~200ms/pageMedia-altaBassoTracking di massa, keyword list > 100
Playwright + proxy residenziali~1.5s/pageAltaMedioSERP JS-heavy, challenge avanzate
Amazon SP-API (Product/Reports)n/a (API)Massima (ufficiale)Gratuito per sellerQuando serve solo catalog/own data

Nota: lo SP-API non espone direttamente la posizione organica per keyword arbitraria; per quello serve il SERP scraping. Combina i due quando possibile. Per pattern di scraping generali vedi web scraping e SERP tracking; per dettagli tecnici consulta la documentazione ProxyHat.

Errori comuni e edge case

  • Posizione sponsorizzata contata come organica: il 30–50% delle prime posizioni su keyword commerciali è Sponsored. Separa sempre i due flussi.
  • Marketplace sbagliato: amazon.com con IP IT può restituire risultati localizzati o redirect. Forza -country-US e Accept-Language: en-US.
  • Sessione non sticky: pagine 1 e 2 con IP diverso possono mostrare ordering differente. Usa -session-{id} per tutta la keyword.
  • Paginazione oltre page 5–7: Amazon spesso non serve oltre; tronca e registra NULL.
  • Variazioni di layout: A/B test di Amazon cambiano selettori; aggiungi test di smoke sul parser.

Etica e conformità

  • Traccia i tuoi listing o dati pubblici; evita scraping massivo di concorrenti per scopi non autorizzati.
  • Throttle ragionevole: non saturare la capacità del marketplace.
  • Rispetta robots.txt e i ToS; considera lo SP-API per dati ufficiali.
  • Non memorizzare dati personali (recensori); se lo fai, applica GDPR (base giuridica, conservazione).

Key takeaways

  • Amazon è un SERP proprietario: traccia posizione organica per keyword, marketplace e ASIN.
  • Proxy residenziali con geo-targeting (-country-US/DE/IT) e sessioni sticky sono obbligatori.
  • Parser basato su [data-component-type="s-search-result"] + data-asin + label "Sponsored".
  • curl_cffi per velocità, Playwright per robustezza; SP-API per dati ufficiali.
  • Production: throttle, retry/backoff, CAPTCHA detection, indexation check, monitoring del tasso di successo.

FAQ

Cos'è il monitoraggio posizionamento keyword Amazon con proxy?

È la raccolta periodica del SERP Amazon tramite proxy residenziali geo-localizzati, il parsing della posizione organica di un ASIN per keyword e marketplace, e la memorizzazione dello storico per analisi SEO e competitive.

Perché è importante per chi usa proxy?

Perché l'algoritmo A9 premia sales velocity e rilevanza, e la posizione varia per marketplace; senza proxy residenziali con geo-targeting i risultati sono localizzati in modo inconsistente e i soft-block sono frequenti.

Quale tipo di proxy è migliore?

Proxy residenziali con geo-targeting per paese e sessioni sticky: IP ISP reali, localizzazione per-marketplace stabile, rotazione controllata per evitare anti-bot.

Come evitare i blocchi?

Throttle 1–2 req/s, retry con backoff esponenziale, detection di CAPTCHA/redirect, header realistici e rotazione di sessione per keyword. Monitora il tasso di successo e fermati sotto il 90%.

Playwright o curl_cffi?

curl_cffi per tracking di massa (~200ms/page); Playwright per SERP JS-heavy o challenge avanzate (~1.5s/page). Combina con SP-API per dati ufficiali quando disponibili.

Pronto per iniziare?

Accedi a oltre 50M di IP residenziali in oltre 148 paesi con filtraggio AI.

Vedi i prezziProxy residenziali
← Torna al Blog