Юридическая заметка: Эта статья посвящена сбору публично доступных данных поисковой выдачи Amazon. В США несанкционированный доступ к компьютерным системам регулируется CFAA (18 U.S.C. § 1030), а в ЕС обработка персональных данных подпадает под GDPR. Соблюдайте Условия использования Amazon, не обходите технические средства защиты и не собирайте персональные данные. Для коммерческого использования рассмотрите официальный Amazon SP-API.
Отслеживание позиций ключевых слов Amazon через прокси — критически важная задача для продавцов на Amazon и marketplace SEO-разработчиков. Позиция товара в поисковой выдаче напрямую влияет на видимость, клики и продажи. В этом руководстве мы разберём, как построить надёжный amazon keyword rank tracker на Python, используя residential-прокси для обхода гео-ограничений и анти-бот защиты Amazon.
Почему Amazon — это отдельный SERP: отслеживание позиций ключевых слов через прокси
Amazon — не просто поисковик; это торговая платформа, где ранжирование определяется комбинацией релевантности и коммерческих факторов. В отличие от Google, где основную роль играют ссылки и контент, алгоритм Amazon A9 учитывает:
- Исторические продажи (sales velocity) — товары с высоким объёмом продаж ранжируются выше
- Релевантность запроса — соответствие названия, буллетов и описания поисковому запросу
- Click-through rate и conversion rate — кликабельность и конверсия карточки
- Доступность и цену — out-of-stock товары понижаются в выдаче
Для продавца это означает: позиция по ключевому слову на странице 1 и странице 5 — разница в десятки процентов выручки. Поэтому необходимо регулярно отслеживать органическую позицию каждого ASIN по целевым ключевым словам, в конкретном marketplace (amazon.com, amazon.de, amazon.co.uk) и в конкретном регионе доставки.
Amazon SERP scraping осложняется тем, что результаты локализованы: один и тот же запрос в Германии и в США даёт разные товары на разных позициях. Кроме того, Amazon активно применяет анти-бот защиту — CAPTCHA, rate limiting и блокировка подозрительных IP-адресов. По данным исследований, datacenter-прокси имеют success rate всего 40–70% на Amazon, тогда как residential-прокси достигают 95–99%.
Парсинг страницы результатов Amazon
Страница результатов поиска Amazon имеет относительно стабильную HTML-структуру. Каждый результат поиска заключён в элемент с атрибутом data-component-type="s-search-result". Внутри этого элемента:
data-asin— идентификатор товара (ASIN)- Текст "Sponsored" или локализованный аналог — признак платного размещения
- Заголовок и цена — во вложенных элементах
Органическая позиция — это порядковый номер результата, исключая спонсированные размещения. Например, если на странице 10 результатов, из которых 2 спонсированных, органическая позиция ASIN может быть от 1 до 8.
Разметка: что искать в HTML
<div data-component-type="s-search-result" data-asin="B0XXXXXXXX">
<div data-component-type="sp-sponsored-result"> ... </div>
<span>Sponsored</span>
<h2><a href="/dp/B0XXXXXXXX">Product Title</a></h2>
<span class="a-price">$29.99</span>
</div>
Ключевые признаки спонсированного размещения: текст "Sponsored" (или локализованный аналог — "Реклама", "Gesponsert", "Sponsorisé"), а также атрибут data-component-type="sp-sponsored-result" внутри карточки.
Почему нужны residential-прокси с гео-таргетингом
Amazon локализует выдачу по IP-адресу и marketplace. Запрос из дата-центра в США к amazon.de может вернуть упрощённую или искажённую выдачу. Для точного отслеживания позиций необходимо:
- Гео-таргетинг по стране — residential IP в той же стране, что и marketplace
- Sticky-сессии — один IP для всех страниц одного запроса (пагинация)
- Ротация между запросами — разные IP для разных ключевых слов, чтобы избежать rate limiting
ProxyHat предоставляет residential-прокси с гео-таргетингом через параметры в username:
user-country-US— IP из США для amazon.comuser-country-DE— IP из Германии для amazon.deuser-session-abc123— sticky-сессия для стабильной пагинации
Datacenter-прокси чаще блокируются Amazon, потому что IP-диапазоны дата-центров хорошо известны. Residential-прокси используют IP реальных провайдеров, что делает их неотличимыми от обычных пользователей. Подробнее о типах прокси — на странице use-cases web scraping.
Практическая реализация: отслеживание позиций Amazon на Python
Ниже — несколько рабочих примеров: от простого curl до production-скрипта с Playwright. Каждый пример использует ProxyHat на gate.proxyhat.com:8080.
1. Базовый запрос через curl с ProxyHat
# Поиск на amazon.com через residential-прокси США
curl -s \
-x "http://user-country-US: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+earbuds&page=1"
2. Python: curl_cffi + ProxyHat (имперсонация браузера)
Библиотека curl_cffi имитирует TLS-отпечаток реального браузера, что снижает риск блокировок. Сравните: raw-прокси (без гео-таргетинга) и ProxyHat с country-флагом:
from curl_cffi import requests
# --- Raw proxy (без гео-таргетинга) ---
raw_proxy = "http://user:pass@gate.proxyhat.com:8080"
# --- ProxyHat с гео-таргетингом и sticky-сессией ---
def make_proxyhat_url(country: str, session_id: str,
user: str = "user", pw: str = "pass") -> str:
"""Создаёт URL прокси ProxyHat с country и session флагами."""
username = f"{user}-country-{country}-session-{session_id}"
return f"http://{username}:{pw}@gate.proxyhat.com:8080"
def fetch_amazon_page(keyword: str, page: int, country: str = "US") -> str:
session_id = f"kw-{keyword.replace(' ', '-')}-pg{page}"
proxy = make_proxyhat_url(country, 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",
}
marketplace_domains = {
"US": "amazon.com", "DE": "amazon.de", "UK": "amazon.co.uk",
"FR": "amazon.fr", "IT": "amazon.it", "ES": "amazon.es",
"JP": "amazon.co.jp",
}
domain = marketplace_domains.get(country, "amazon.com")
url = f"https://www.{domain}/s?k={keyword.replace(' ', '+')}&page={page}"
response = requests.get(url, headers=headers, proxy=proxy,
impersonate="chrome120", timeout=30)
response.raise_for_status()
return response.text
3. Парсинг SERP: извлечение ASIN, позиции и Sponsored
from bs4 import BeautifulSoup
from dataclasses import dataclass
from typing import List
@dataclass
class AmazonResult:
asin: str
organic_position: int
is_sponsored: bool
title: str
page: int
SPONSORED_LABELS = {"Sponsored", "Реклама", "Gesponsert",
"Sponsorisé", "Sponsorizzato", "Patrocinado"}
def parse_amazon_serp(html: str, page: int) -> List[AmazonResult]:
soup = BeautifulSoup(html, "html.parser")
results = []
organic_counter = 0
for div in soup.select('[data-component-type="s-search-result"]'):
asin = div.get("data-asin", "")
if not asin:
continue
is_sponsored = False
for label in div.find_all(string=True):
if label.strip() in SPONSORED_LABELS:
is_sponsored = True
break
sponsored_div = div.select_one(
'[data-component-type="sp-sponsored-result"]'
)
if sponsored_div:
is_sponsored = True
title_tag = div.select_one("h2 a span") or div.select_one("h2 span")
title = title_tag.get_text(strip=True) if title_tag else ""
if not is_sponsored:
organic_counter += 1
results.append(AmazonResult(
asin=asin,
organic_position=organic_counter if not is_sponsored else 0,
is_sponsored=is_sponsored,
title=title,
page=page,
))
return results
def find_asin_position(results: List[AmazonResult],
target_asin: str) -> dict:
"""Находит позицию целевого ASIN в органической выдаче."""
for r in results:
if r.asin == target_asin and not r.is_sponsored:
return {
"found": True,
"organic_position": r.organic_position,
"page": r.page,
"is_sponsored": False,
}
if r.asin == target_asin and r.is_sponsored:
return {
"found": True,
"organic_position": 0,
"page": r.page,
"is_sponsored": True,
}
return {"found": False, "organic_position": 0, "page": 0}
4. Playwright + ProxyHat: для JS-рендеренных страниц
Иногда Amazon отдаёт динамический контент, требующий рендеринга JavaScript. Playwright справляется с этим, используя прокси через launch-параметры:
import asyncio
from playwright.async_api import async_playwright
async def track_keyword_playwright(
keyword: str,
target_asin: str,
country: str = "US",
max_pages: int = 5,
) -> list[dict]:
marketplace_domains = {
"US": "amazon.com", "DE": "amazon.de", "UK": "amazon.co.uk",
"FR": "amazon.fr", "IT": "amazon.it", "ES": "amazon.es",
}
domain = marketplace_domains.get(country, "amazon.com")
positions = []
session_id = f"track-{keyword.replace(' ', '-')}-{country}"
proxy_url = (
f"http://user-country-{country}-session-{session_id}"
":pass@gate.proxyhat.com:8080"
)
async with async_playwright() as p:
browser = await p.chromium.launch(
proxy={"server": proxy_url},
headless=True,
)
context = await browser.new_context(
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"
),
locale="en-US" if country == "US" else "de-DE",
)
for page_num in range(1, max_pages + 1):
url = (
f"https://www.{domain}/s?k="
f"{keyword.replace(' ', '+')}&page={page_num}"
)
page = await context.new_page()
try:
await page.goto(url, wait_until="domcontentloaded",
timeout=30000)
await page.wait_for_selector(
'[data-component-type="s-search-result"]',
timeout=10000,
)
html = await page.content()
results = parse_amazon_serp(html, page_num)
pos = find_asin_position(results, target_asin)
if pos["found"]:
positions.append({
"keyword": keyword,
"asin": target_asin,
"page": pos["page"],
"organic_position": pos["organic_position"],
"sponsored": pos["is_sponsored"],
"country": country,
})
break
except Exception as e:
print(f"Page {page_num} error: {e}")
finally:
await page.close()
await browser.close()
return positions
# Запуск
# asyncio.run(track_keyword_playwright("wireless earbuds", "B0DXXXXXXX", "US"))
5. Production-скрипт: планировщик с retry, backoff и историей позиций
import asyncio
import json
import logging
import sqlite3
from datetime import datetime, timezone
from typing import Optional
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
)
logger = logging.getLogger("amazon-tracker")
DB_PATH = "rank_history.db"
def init_db():
conn = sqlite3.connect(DB_PATH)
conn.execute("""
CREATE TABLE IF NOT EXISTS rank_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
keyword TEXT NOT NULL,
asin TEXT NOT NULL,
country TEXT NOT NULL,
organic_position INTEGER,
page INTEGER,
is_sponsored INTEGER,
found INTEGER,
tracked_at TEXT NOT NULL
)
""")
conn.commit()
conn.close()
async def fetch_with_retry(
keyword: str,
page: int,
country: str,
max_retries: int = 3,
) -> Optional[str]:
"""Fetch с экспоненциальным backoff."""
for attempt in range(max_retries):
try:
html = fetch_amazon_page(keyword, page, country)
if "captcha" in html.lower() or "robot" in html.lower():
logger.warning(f"CAPTCHA detected on page {page}, "
f"attempt {attempt + 1}")
await asyncio.sleep(2 ** (attempt + 2))
continue
return html
except Exception as e:
logger.error(f"Fetch error page {page}: {e}")
wait = 2 ** (attempt + 1)
logger.info(f"Retrying in {wait}s...")
await asyncio.sleep(wait)
return None
async def track_keyword(
keyword: str,
target_asin: str,
country: str = "US",
max_pages: int = 5,
) -> dict:
"""Отслеживает позицию ASIN по ключевому слову на 1-5 страницах."""
init_db()
for page_num in range(1, max_pages + 1):
html = await fetch_with_retry(keyword, page_num, country)
if not html:
logger.warning(f"Failed to fetch page {page_num}")
continue
results = parse_amazon_serp(html, page_num)
pos = find_asin_position(results, target_asin)
if pos["found"]:
record = {
"keyword": keyword,
"asin": target_asin,
"country": country,
"organic_position": pos["organic_position"],
"page": pos["page"],
"is_sponsored": pos["is_sponsored"],
"found": True,
"tracked_at": datetime.now(timezone.utc).isoformat(),
}
_save_record(record)
logger.info(
f"Found {target_asin} for '{keyword}' "
f"at organic pos {pos['organic_position']} "
f"on page {pos['page']} ({country})"
)
return record
record = {
"keyword": keyword,
"asin": target_asin,
"country": country,
"organic_position": 0,
"page": 0,
"is_sponsored": False,
"found": False,
"tracked_at": datetime.now(timezone.utc).isoformat(),
}
_save_record(record)
logger.warning(f"ASIN {target_asin} not found for '{keyword}' "
f"in {max_pages} pages ({country})")
return record
def _save_record(record: dict):
conn = sqlite3.connect(DB_PATH)
conn.execute(
"INSERT INTO rank_history "
"(keyword, asin, country, organic_position, page, "
"is_sponsored, found, tracked_at) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
(
record["keyword"], record["asin"], record["country"],
record["organic_position"], record["page"],
int(record["is_sponsored"]), int(record["found"]),
record["tracked_at"],
),
)
conn.commit()
conn.close()
# Пример запуска
# asyncio.run(track_keyword("wireless earbuds", "B0DXXXXXXX", "US", 5))
Production-настройки: что нужно знать
Ежедневное расписание
Позиции на Amazon меняются ежедневно. Используйте APScheduler или cron для регулярного запуска:
# cron: каждый день в 06:00 UTC
0 6 * * * /usr/bin/python3 /path/to/amazon_tracker.py
Retry и backoff
Amazon может вернуть 503 или CAPTCHA. Стандартная стратегия — экспоненциальный backoff: 2s, 4s, 8s между попытками. Не более 3 retry на страницу. Если CAPTCHA повторяется — смените сессию (новый session ID = новый IP).
Обнаружение CAPTCHA
Amazon перенаправляет на /captcha/ или показывает страницу с заголовком "Robot Check". Проверяйте URL и контент:
def is_captcha_page(html: str, url: str) -> bool:
if "/captcha/" in url:
return True
lower = html.lower()
indicators = ["robot check", "captcha", "automated access"]
return any(ind in lower for ind in indicators)
Проверка индексации
Если ASIN не найден ни на 5 страницах — возможно, он не индексируется по этому ключевому слову. Проверьте:
- Существует ли карточка товара (direct URL:
/dp/ASIN) - Доступен ли товар в выбранном marketplace
- Не заблокирован ли ASIN
Управление concurrency
Не делайте более 10 параллельных запросов с одного IP. ProxyHat позволяет до 50 параллельных сессий на аккаунт. Используйте asyncio.Semaphore для ограничения:
sem = asyncio.Semaphore(10) # max 10 concurrent requests
async def limited_fetch(keyword, page, country):
async with sem:
return await fetch_with_retry(keyword, page, country)
Сравнение: residential vs datacenter vs mobile прокси для Amazon
| Тип прокси | Success rate на Amazon | Скорость | Риск блокировки | Стоимость |
|---|---|---|---|---|
| Residential | 95–99% | ~200ms | Низкий | Средняя |
| Datacenter | 40–70% | ~50ms | Высокий | Низкая |
| Mobile | 98–99% | ~300ms | Очень низкий | Высокая |
Для track amazon rankings python оптимальный выбор — residential-прокси: баланс надёжности и стоимости. Mobile-прокси дают максимальный success rate, но стоят дороже. Datacenter-прокси не рекомендуются для Amazon — блокировки слишком часты.
Этика и легальность
Track amazon rankings python — мощный инструмент, но используйте его ответственно:
- Отслеживайте свои и публичные листинги. Не собирайте данные конкурентов с целью недобросовестной конкуренции.
- Соблюдайте robots.txt. Проверьте
https://www.amazon.com/robots.txtперед скрапингом. - Троттлите запросы. Не более 1 запроса в 2–3 секунды на одну сессию. Это снижает нагрузку на серверы Amazon.
- Рассмотрите Amazon SP-API. Для коммерческого использования у Amazon есть официальный SP-API с эндпоинтами для аналитики каталога. SP-API требует регистрации продавца и одобрения, но легален и стабилен.
- GDPR. Если вы собираете данные в ЕС, убедитесь, что не обрабатываете персональные данные (отзывы с именами пользователей и т.д.). Подробнее — на gdpr-info.eu.
Ключевые выводы
- Amazon SERP scraping требует residential-прокси с гео-таргетингом по стране marketplace
- Парсинг: ищите
[data-component-type="s-search-result"], читайтеdata-asin, отделяйте Sponsored от органики- Sticky-сессии (
-session-id) необходимы для корректной пагинации — один IP на все страницы одного запроса- Production: ежедневное расписание, retry с backoff, CAPTCHA-детекция, проверка индексации
- Этика: троттлите запросы, соблюдайте ToS, рассмотрите SP-API для коммерческого использования
Готовы начать? Изучите тарифы ProxyHat, проверьте доступные локации и прочитайте о SERP tracking и web scraping. Полная документация по подключению — на docs.proxyhat.com.






