Nota prawna: Ten przewodnik dotyczy wyłącznie publicznie dostępnych danych ze stron wyników wyszukiwania Amazon. W USA nieautoryzowany dostęp do systemów komputerowych może naruszać Computer Fraud and Abuse Act (CFAA). W UE przetwarzanie danych osobowych podlega RODO (GDPR). Śledź własne listingi lub publiczne dane z poszanowaniem warunków korzystania Amazon i ograniczeń robots.txt. Nie omijaj mechanizmów kontroli dostępu ani nie pobieraj danych konkurencji w skali masowej.
Amazon Keyword Rank Tracking z proxy — od czego zacząć
Amazon Keyword Rank Tracking z proxy to proces regularnego pobierania stron wyników wyszukiwania Amazon (SERP) dla wybranych słów kluczowych, parsowania pozycji organicznych i sponsored oraz zapisywania historii pozycji docelowego ASIN w czasie. W przeciwieństwie do Google, Amazon to marketplace — wyniki są napędzane przez relevance i sales velocity, a nie linki. Dla sprzedawców i marketplace SEO developerów śledzenie pozycji per keyword, marketplace i ASIN jest kluczowe, bo zmiana pozycji z pozycji 3 na 15 może oznaczać spadek sprzedaży o 50% lub więcej.
Amazon SERP scraping wymaga proxy residencjalnych z dwóch powodów: (1) Amazon lokalizuje wyniki na poziomie marketplace (amazon.com vs amazon.de vs amazon.co.uk) i na poziomie regionu w obrębie marketplace; (2) Amazon stosuje agresywne mechanizmy anti-bot, które blokują lub wyświetlają CAPTCHA dla podejrzanych adresów IP, zwłaszcza datacenter. Proxy residencjalne z geo-targetingiem kraju i sticky sesjami rozwiązują oba problemy.
Dlaczego Amazon search to osobny SERP
Amazon to nie Google. Algorytm rankingowy A9 (obecnie często nazywany A10) opiera się na:
- Relevance — dopasowanie tytułu, bullet points, opisu i backend keywords do zapytania.
- Sales velocity — historia konwersji i sprzedaży dla danego keywordu; produkty, które sprzedają się lepiej, rankują wyżej.
- Performance metrics — CTR, add-to-cart rate, recenzje, dostępność.
Dlatego sprzedawcy muszą śledzić organic position per keyword, marketplace i ASIN. Pozycja zależy od kombinacji tych trzech wymiarów — ASIN może być na pozycji 1 dla „wireless mouse" na amazon.com i na pozycji 40 dla tego samego keywordu na amazon.de. Bez proxy residencjalnych z geo-targetingiem nie da się uzyskać wiarygodnych danych.
Według analiz Amazon Science i publicznych badań marketplace SEO, pozycje organiczne na Amazonie zmieniają się częściej niż w Google — nawet kilka razy dziennie w zależności od inventory i sales velocity. Codzienne śledzenie jest minimum; godzinowe dla konkurencyjnych nisz.
Parsowanie strony wyników Amazon
Strona wyników Amazon (np. https://www.amazon.com/s?k=wireless+mouse) renderuje listę produktów w elementach z atrybutem data-component-type="s-search-result". Każdy taki element zawiera:
data-asin— identyfikator ASIN produktu.- Tytuł produktu w elemencie
h2 > alub.a-text-normal. - Etykietę Sponsored dla płatnych placementów — zwykle w elemencie z tekstem „Sponsored" lub atrybutem wskazującym na reklamę.
- Cenę, rating, liczbę recenzji.
Kluczowa logika:
- Iteruj po wszystkich elementach
[data-component-type="s-search-result"]. - Odczytaj
data-asinz każdego elementu. - Sprawdź, czy element zawiera etykietę „Sponsored" — jeśli tak, oznacz jako paid, nie organic.
- Oblicz organic position jako licznik elementów organicznych (pomijając sponsored).
- Zapisz pozycję docelowego ASIN.
Amazon często wstawia placementy Sponsored między wyniki organiczne, dlatego musisz oddzielić paid od organic — inaczej pozycje będą zniekształcone.
Dlaczego proxy residencjalne są konieczne
Amazon stosuje wielowarstwowe mechanizmy obronne:
| Warstwa | Zachowanie | Mitigacja |
|---|---|---|
| Geo-lokalizacja IP | Wyniki zależą od kraju IP — amazon.com z IP z Niemiec może pokazywać inne wyniki niż z IP z USA. | Proxy residencjalne z geo-targetingiem kraju (np. -country-US). |
| Detection datacenter IP | Amazon blokuje lub CAPTCHA-uje zapytania z znanych bloków datacenter (AWS, DigitalOcean, Hetzner). | Proxy residencjalne z prawdziwych IP ISP. |
| Rate limiting | Zbyt wiele zapytań z jednego IP w krótkim czasie wywołuje CAPTCHA lub 503. | Rotacja sesji, throttling 1-2 req/s. |
| Paginacja zależna od sesji | Strony 2-5 wymagają spójnego session ID i cookies. | Sticky sesje z flagą -session-. |
Proxy datacenter są tańsze, ale Amazon wykrywa je szybko — sukces rate spada często poniżej 30% po kilkudziesięciu zapytaniach. Proxy residencjalne utrzymują sukces rate powyżej 90% przy rozsądnym throttlingu. Więcej o typach proxy znajdziesz w naszym use case web scraping.
ProxyHat: geo-targeting i sticky sesje
ProxyHat używa jednego gateway endpoint: gate.proxyhat.com:8080 (HTTP) lub gate.proxyhat.com:1080 (SOCKS5). Geo-targeting i sesje konfiguruje się w username:
user-country-US:pass— ruch z IP w USA (dla amazon.com).user-country-DE:pass— ruch z IP w Niemczech (dla amazon.de).user-country-GB:pass— ruch z IP w UK (dla amazon.co.uk).user-session-abc123:pass— sticky sesja dla stabilnej paginacji.user-country-US-session-abc123:pass— kombinacja kraju i sesji.
Dla paginacji przez strony 1-5 używaj tego samego session ID — inaczej Amazon może zresetować sesję i pokazać inne wyniki. Pełną listę krajów znajdziesz na stronie locations, a cennik na pricing. Dokumentację techniczną znajdziesz na docs.proxyhat.com.
Przykład 1: curl z proxy ProxyHat
Zacznijmy od najprostszego przypadku — pobrania pierwszej strony wyników dla „wireless mouse" z amazon.com:
# Pobierz stronę 1 dla keywordu "wireless mouse" z IP w USA
curl -x http://user-country-US-session-track1:pass@gate.proxyhat.com:8080 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
-H "Accept-Language: en-US,en;q=0.9" \
"https://www.amazon.com/s?k=wireless+mouse&page=1" \
-o amazon_page1.html
Zwróć uwagę na flagę -session-track1 — użyj jej samej dla stron 2-5, aby utrzymać spójną sesję.
Przykład 2: Python z curl_cffi i ProxyHat
curl_cffi to biblioteka Python, która emuluje TLS fingerprint przeglądarek — kluczowe dla omijania anti-bot heurystyk Amazon. Zainstaluj:
pip install curl_cffi lxml
Pełny przykład pobierania stron 1-5, parsowania pozycji i zapisywania historii:
import time
import json
import random
from datetime import datetime, timezone
from pathlib import Path
from lxml import html
from curl_cffi import requests
# Konfiguracja ProxyHat
PROXYHAT_USER = "user-country-US-session-{sid}"
PROXYHAT_PASS = "pass"
PROXYHAT_GATE = "gate.proxyhat.com:8080"
TARGET_ASIN = "B07G9RZMG1" # przykładowy ASIN
KEYWORD = "wireless mouse"
MARKETPLACE = "https://www.amazon.com"
MAX_PAGES = 5
def build_proxy_url(session_id: str) -> str:
user = PROXYHAT_USER.format(sid=session_id)
return f"http://{user}:{PROXYHAT_PASS}@{PROXYHAT_GATE}"
def fetch_page(keyword: str, page: int, session_id: str) -> str | None:
"""Pobierz stronę wyników Amazon z proxy ProxyHat."""
url = f"{MARKETPLACE}/s?k={keyword.replace(' ', '+')}&page={page}"
proxies = {"http": build_proxy_url(session_id), "https": build_proxy_url(session_id)}
headers = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.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",
}
try:
resp = requests.get(url, proxies=proxies, headers=headers, impersonate="chrome120", timeout=30)
if resp.status_code == 200:
return resp.text
print(f"[WARN] HTTP {resp.status_code} dla strony {page}")
return None
except Exception as e:
print(f"[ERROR] {e}")
return None
def parse_serp(html_text: str) -> list[dict]:
"""Parsuj stronę wyników — zwróć listę pozycji z ASIN i typem (organic/sponsored)."""
tree = html.fromstring(html_text)
results = []
organic_pos = 0
for el in tree.cssselect('[data-component-type="s-search-result"]'):
asin = el.get("data-asin", "")
if not asin:
continue
# Detekcja Sponsored
sponsored_els = el.cssselect(".puis-label-popover-default, .a-color-secondary")
is_sponsored = any("Sponsored" in (e.text_content() or "") for e in sponsored_els)
if not is_sponsored:
organic_pos += 1
pos = organic_pos
placement = "organic"
else:
pos = None
placement = "sponsored"
# Tytuł
title_el = el.cssselect("h2 a")
title = title_el[0].text_content().strip() if title_el else ""
results.append({
"asin": asin,
"position": pos,
"placement": placement,
"title": title,
})
return results
def find_target_position(results: list[dict], target_asin: str) -> dict | None:
for r in results:
if r["asin"] == target_asin:
return r
return None
def track_keyword(keyword: str, target_asin: str, session_id: str) -> dict:
"""Pobierz strony 1-5 i znajdź pozycję docelowego ASIN."""
all_results = []
for page in range(1, MAX_PAGES + 1):
html_text = fetch_page(keyword, page, session_id)
if not html_text:
break
if "captcha" in html_text.lower() or "robot" in html_text.lower():
print(f"[CAPTCHA] Wykryto CAPTCHA na stronie {page}")
break
page_results = parse_serp(html_text)
all_results.extend(page_results)
time.sleep(random.uniform(1.5, 3.0)) # throttling
target = find_target_position(all_results, target_asin)
return {
"timestamp": datetime.now(timezone.utc).isoformat(),
"keyword": keyword,
"target_asin": target_asin,
"marketplace": MARKETPLACE,
"found": target is not None,
"position": target["position"] if target else None,
"placement": target["placement"] if target else None,
"total_organic": sum(1 for r in all_results if r["placement"] == "organic"),
}
if __name__ == "__main__":
session_id = f"track{int(time.time())}"
result = track_keyword(KEYWORD, TARGET_ASIN, session_id)
print(json.dumps(result, indent=2))
# Zapis do historii
history_file = Path("rank_history.jsonl")
with history_file.open("a", encoding="utf-8") as f:
f.write(json.dumps(result) + "\n")
Ten skrypt: pobiera strony 1-5 z tym samym session ID, parsuje pozycje organic vs sponsored, wykrywa CAPTCHA, zapisuje wynik do pliku JSONL. Throttling 1.5-3 sekundy między stronami zmniejsza ryzyko blokady.
Przykład 3: Retry z backoff i circuit breaker
W produkcji musisz obsłużyć błędy sieci i CAPTCHA. Oto warstwa retry z exponential backoff:
import time
from functools import wraps
class CircuitBreakerOpen(Exception):
pass
class CircuitBreaker:
def __init__(self, threshold: int = 5, reset_timeout: int = 60):
self.threshold = threshold
self.reset_timeout = reset_timeout
self.failures = 0
self.opened_at = None
def record_failure(self):
self.failures += 1
if self.failures >= self.threshold:
self.opened_at = time.time()
def record_success(self):
self.failures = 0
self.opened_at = None
def can_proceed(self) -> bool:
if self.opened_at is None:
return True
if time.time() - self.opened_at > self.reset_timeout:
self.opened_at = None
self.failures = 0
return True
return False
def retry_with_backoff(max_retries: int = 3, base_delay: float = 2.0):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
delay = base_delay
for attempt in range(max_retries):
try:
result = func(*args, **kwargs)
return result
except Exception as e:
print(f"[RETRY] Próba {attempt+1}/{max_retries}: {e}")
time.sleep(delay + random.uniform(0, 1))
delay *= 2
raise RuntimeError(f"Nie udało się po {max_retries} próbach")
return wrapper
return decorator
# Użycie:
breaker = CircuitBreaker(threshold=5, reset_timeout=60)
@retry_with_backoff(max_retries=3, base_delay=2.0)
def fetch_page_safe(keyword, page, session_id):
if not breaker.can_proceed():
raise CircuitBreakerOpen("Circuit breaker otwarty — poczekaj 60s")
html_text = fetch_page(keyword, page, session_id)
if html_text is None:
breaker.record_failure()
raise RuntimeError("Brak HTML")
breaker.record_success()
return html_text
Przykład 4: Playwright z proxy SOCKS5 ProxyHat
Gdy Amazon wymaga pełnego renderowania JS (rzadziej, ale się zdarza przy dynamicznych komponentach), użyj Playwright z proxy SOCKS5:
pip install playwright lxml
playwright install chromium
import asyncio
from datetime import datetime, timezone
from playwright.async_api import async_playwright
from lxml import html
PROXYHAT_USER = "user-country-US-session-pw1"
PROXYHAT_PASS = "pass"
async def fetch_with_playwright(keyword: str, page: int) -> str:
url = f"https://www.amazon.com/s?k={keyword.replace(' ', '+')}&page={page}"
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
proxy={
"server": "socks5://gate.proxyhat.com:1080",
"username": PROXYHAT_USER,
"password": PROXYHAT_PASS,
},
)
context = await browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36",
locale="en-US",
)
pg = await context.new_page()
try:
await pg.goto(url, timeout=30000, wait_until="domcontentloaded")
content = await pg.content()
return content
finally:
await browser.close()
async def main():
html_text = await fetch_with_playwright("wireless mouse", 1)
results = parse_serp(html_text) # parse_serp z Przykładu 2
print(f"Znaleziono {len(results)} wyników")
if __name__ == "__main__":
asyncio.run(main())
Przykład 5: Harmonogram i sprawdzanie indeksacji
Codzienne uruchamianie i wykrywanie, czy ASIN w ogóle rankuje (indeksacja):
import schedule
import json
from pathlib import Path
KEYWORDS = ["wireless mouse", "ergonomic mouse", "gaming mouse"]
TARGET_ASIN = "B07G9RZMG1"
def daily_tracking_job():
session_id = f"daily{int(time.time())}"
for kw in KEYWORDS:
result = track_keyword(kw, TARGET_ASIN, session_id)
# Sprawdzenie indeksacji — czy ASIN w ogóle pojawia się w wynikach?
if not result["found"]:
print(f"[INDEX] ASIN {TARGET_ASIN} nie rankuje dla '{kw}' — możliwy problem z indeksacją")
with Path("rank_history.jsonl").open("a", encoding="utf-8") as f:
f.write(json.dumps(result) + "\n")
time.sleep(random.uniform(5, 10)) # pauza między keywordami
# Harmonogram: codziennie o 9:00 UTC
schedule.every().day.at("09:00").do(daily_tracking_job)
if __name__ == "__main__":
while True:
schedule.run_pending()
time.sleep(60)
Sprawdzanie indeksacji jest krytyczne — jeśli ASIN nagle nie pojawia się w wynikach dla keywordu, na którym wcześniej rankował, może to oznaczać: (1) deindeksację przez Amazon, (2) zmianę algorytmu, (3) problem z inventory, (4) blokadę konta. Szybkie wykrycie pozwala reagować.
Przykład 6: Node.js z proxy ProxyHat
Dla zespołów JS/TypeScript:
import { fetch } from "undici";
import * as cheerio from "cheerio";
const PROXYHAT_GATE = "gate.proxyhat.com:8080";
const SESSION_ID = "node-track1";
async function fetchAmazonPage(keyword, page) {
const proxyUrl = `http://user-country-US-session-${SESSION_ID}:pass@${PROXYHAT_GATE}`;
const url = `https://www.amazon.com/s?k=${encodeURIComponent(keyword)}&page=${page}`;
const resp = await fetch(url, {
dispatcher: new (await import("undici")).ProxyAgent(proxyUrl),
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
},
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
return await resp.text();
}
function parseSerp(htmlText) {
const $ = cheerio.load(htmlText);
const results = [];
let organicPos = 0;
$('[data-component-type="s-search-result"]').each((_, el) => {
const asin = $(el).attr("data-asin");
if (!asin) return;
const text = $(el).text();
const isSponsored = text.includes("Sponsored");
if (!isSponsored) organicPos++;
results.push({ asin, position: isSponsored ? null : organicPos, placement: isSponsored ? "sponsored" : "organic" });
});
return results;
}
async function main() {
const html = await fetchAmazonPage("wireless mouse", 1);
const results = parseSerp(html);
console.log(`Znaleziono ${results.length} wyników`);
}
main().catch(console.error);
Wskazówki produkcyjne
- Throttling: 1-2 żądania na sekundę per sesja. Amazon toleruje ludzkie tempo, nie masowe scrapowanie.
- Rotacja sesji: nowa sesja (
-session-) dla każdego keywordu; ta sama sesja dla paginacji 1-5 jednego keywordu. - Detekcja CAPTCHA: sprawdzaj, czy HTML zawiera „captcha", „robot", lub czy status to 503. Wtedy pauzuj 60-120s i zmień sesję.
- Retry z backoff: exponential backoff (2s, 4s, 8s) z jitterem; max 3 retry.
- Circuit breaker: po 5 kolejnych błędach zatrzymaj na 60s — unikniesz kaskady błędów.
- Logging: zapisuj timestamp, keyword, ASIN, pozycję, HTTP status, session ID do JSONL lub bazy.
- Monitorowanie sukces rate: jeśli spada poniżej 80%, zwiększ throttling lub zmień pulę sesji.
- User-Agent rotacja: używaj 3-5 realistycznych UA Chrome/Firefox; unikaj przestarzałych.
- Accept-Language: dopasuj do marketplace —
en-USdla amazon.com,de-DEdla amazon.de.
Typowe błędy i edge case'y
- Mieszanie paid z organic: nie sprawdzasz etykiety „Sponsored" — pozycje są zawyżone. Zawsze oddzielaj.
- Brak sticky sesji: paginacja 2-5 z różnymi IP daje niespójne wyniki — Amazon może pokazać inne produkty.
- Zły kraj IP: amazon.com z IP z Niemiec może pokazać wyniki z europejskim inventory. Używaj
-country-US. - Ignorowanie CAPTCHA: parsowanie strony CAPTCHA daje 0 wyników — sprawdzaj detekcję przed parsowaniem.
- Zbyt szybkie tempo: 10+ req/s z jednej sesji = natychmiastowa blokada. Trzymaj 1-2 req/s.
- Brak sprawdzania indeksacji: ASIN może zniknąć z wyników — bez logowania
found: falsenie zauważysz problemu.
Etyka i alternatywy
- Śledź własne listingi i publiczne dane. Nie pobieraj masowo danych konkurencji — to narusza ToS Amazon i może być nielegalne.
- Respektuj robots.txt i ToS. Amazon pozwala na publiczny dostęp do SERP, ale zakazuje automatycznego scrapowania w komercyjnej skali.
- Throttle human-like. 1-2 req/s to maksimum; codzienne śledzenie kilku keywordów to inna skala niż masowe scrapowanie.
- Rozważ Amazon SP-API. Dla sprzedawców zarejestrowanych w Amazon Brand Registry, Amazon SP-API oferuje oficjalny dostęp do danych katalogu, raportów i (w ograniczonym zakresie) danych search — bezpieczniejsza i legalna alternatywa. Track amazon rankings python nie zastępuje SP-API tam, gdzie ono wystarcza.
Kluczowe wnioski
Amazon Keyword Rank Tracking z proxy to nie zbrodnia — to regularne, throttled pobieranie publicznych SERP dla własnych ASIN. Proxy residencjalne z geo-targetingiem kraju i sticky sesjami są konieczne, bo Amazon lokalizuje wyniki i blokuje datacenter IP. Parsuj
[data-component-type="s-search-result"], oddzielaj Sponsored od organic, używajcurl_cffiz TLS impersonation, i zawsze sprawdzaj indeksację. Dla produkcji: retry, circuit breaker, logging i codzienny harmonogram.
- Amazon SERP to osobny ranking — relevance + sales velocity, nie linki.
- Proxy residencjalne > datacenter — sukces rate 90%+ vs często poniżej 30%.
- Geo-targeting kraju (
-country-US,-country-DE) jest obowiązkowy dla wiarygodnych wyników. - Sticky sesje (
-session-) dla paginacji — inaczej niespójne wyniki. - Oddzielaj Sponsored od organic — etykieta „Sponsored" w elemencie.
- Throttle 1-2 req/s, retry z backoff, detekcja CAPTCHA.
- Sprawdzaj indeksację —
found: falseto sygnał alarmowy. - Rozważ SP-API, jeśli jesteś zarejestrowanym sprzedawcą.
Gotowy zacząć? Zobacz cennik ProxyHat, dostępne lokalizacje i SERP tracking use case. Pełną dokumentację znajdziesz na docs.proxyhat.com.





