Парсинг Wildberries через резидентные прокси: как обойти антибот и не получить блокировку по MAC-адресу
Содержание
- Как устроен антибот Wildberries
- Миф о MAC-адресе
- Реальная схема обхода
- Почему резидентные прокси работают лучше
- Настройка Python-парсера
- Кейс: 403 на третьем запросе
- Кейс: блокировка по отпечатку TLS
- Кейс: ротация IP внутри одной сессии
- Заголовки, которые реально проверяются
- Что делать при блокировке
- Итоговая схема
Wildberries — это не просто маркетплейс. Это гигантская распределённая система с собственным антибот-комплексом, который затачивался годами. Каждый день их инженеры выкатывают обновления, и то, что работало вчера, сегодня даёт 403. Разберём, как выглядит реальная схема обхода и почему MAC-адрес тут ни при чём.
Как устроен антибот Wildberries
Снаружи всё выглядит просто: запрос на карточку товара, JSON в ответе. На деле — трёхуровневая система фильтрации.
Первый уровень — классические заголовки. User-Agent, Accept-Language, Sec-Fetch-*. Тут отсеиваются примитивные скрипты и curl без настроек.
Второй уровень — поведенческий анализ. Частота запросов, паттерны переходов, время между действиями. Если вы долбите один и тот же SKU раз в секунду — вас вычислят за пару минут.
Третий уровень — связывание сессий. Тут уже используются отпечатки TLS, HTTP/2 fingerprinting, порядок заголовков. Именно этот уровень ловит большинство парсеров на Python.
Миф о MAC-адресе
Разберём популярное заблуждение. MAC-адрес — это канальный уровень, он не покидает пределы вашей локальной сети. Маршрутизаторы затирают его при каждом хопе. Wildberries физически не может увидеть ваш MAC.
Откуда растут ноги у этого мифа? Люди путают MAC с аппаратным отпечатком браузера. Canvas fingerprint, WebGL, список шрифтов, разрешение экрана — вот что реально собирается. Плюс WebRTC утекает реальный IP, если не отключить.
Реальная схема обхода
Собираем рабочий стек. Прокси-ротация, правильные заголовки, контроль частоты. Без этого любой антибот раскусит вас за пять минут.
```bash
curl -x http://user:pass@proxy.lexic.ml:8080 \
-H "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" \
-H "Accept: application/json" \
-H "Accept-Language: ru-RU,ru;q=0.9" \
-H "X-Requested-With: XMLHttpRequest" \
"https://www.wildberries.ru/webapi/product/1234567/info"
```
Обратите внимание на порядок заголовков. HTTP/2 не требует строгого порядка, но серверы часто сверяют его с эталонным браузерным. Сбейте порядок — и запрос уйдёт в мусорку.
Почему резидентные прокси работают лучше
Датацентровые IP Wildberries режет на раз. Слишком чистая история, никакого мусора, типичные подсети хостинг-провайдеров. Резидентные адреса выглядят как обычные пользователи: у них есть история, они встречаются в логах рекламных сетей, они живут в реальных подсетях.
Ключевой параметр — время жизни сессии. Один IP на один запрос — это подозрительно. Один IP на десять запросов в течение пяти минут — нормально. Тут помогает липкая сессия: прокси держит один адрес, пока вы не смените его принудительно.
Настройка Python-парсера
Берём aiohttp и настраиваем его под реальную работу. Без синхронных библиотек — только асинхронность, иначе скорость упадёт ниже плинтуса.
```python
import aiohttp
import asyncio
from random import uniform
PROXY = "http://user:pass@proxy.lexic.ml:8080"
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": "application/json",
"Accept-Language": "ru-RU,ru;q=0.9",
}
async def fetch_product(session, sku):
url = f"https://www.wildberries.ru/webapi/product/{sku}/info"
async with session.get(url, headers=HEADERS, proxy=PROXY) as resp:
if resp.status == 200:
return await resp.json()
elif resp.status == 403:
await asyncio.sleep(uniform(10, 20))
return None
return None
async def main():
connector = aiohttp.TCPConnector(limit=5, ttl_dns_cache=300)
async with aiohttp.ClientSession(connector=connector) as session:
for sku in range(1000, 1100):
data = await fetch_product(session, sku)
await asyncio.sleep(uniform(1.5, 3.5))
asyncio.run(main())
```
Тут важна задержка между запросами. Плюс-минус случайное значение, а не фиксированная пауза. Ровные интервалы — верный признак бота.
Кейс: 403 на третьем запросе
Сервер на nginx 1.24, парсер на Node.js, прокси — резидентные. Первые два запроса проходят, третий возвращает 403 с телом `{"error":"access denied"}`.
Причина — отсутствие cookie-сессии. Wildberries выдаёт `__wbl` cookie при первом заходе и ждёт её в последующих запросах. Без обработки cookie-заголовков любой антибот зарубит вас на третьем шаге.
Решение — использовать `aiohttp.CookieJar` с сохранением состояния. Первый запрос идёт на главную страницу, получает cookie, затем уже идут запросы к API. Плюс нужно обрабатывать заголовок `Set-Cookie` при каждом ответе.
Кейс: блокировка по отпечатку TLS
Парсер на Python с библиотекой requests. Всё работает неделю, потом разом падает. Wildberries обновил фильтры и начал распознавать отпечаток TLS от OpenSSL.
Решение — использовать curl_cffi или httpx с поддержкой HTTP/2 и настройкой TLS. Библиотека имитирует отпечаток браузера Chrome, и сервер перестаёт отличать вас от настоящего пользователя.
```python
from curl_cffi import requests
session = requests.Session(impersonate="chrome")
resp = session.get(
"https://www.wildberries.ru/webapi/product/1234567/info",
proxies={"https": "http://user:pass@proxy.lexic.ml:8080"}
)
```
Работает, пока Wildberries не обновит свою базу отпечатков. Это гонка вооружений, и выигрывает тот, кто быстрее адаптируется.
Кейс: ротация IP внутри одной сессии
Парсер собирает цены по 10 000 товаров. Скорость — 50 запросов в минуту. Всё летает, но через час прилетает блокировка на 24 часа.
Причина — все запросы шли через один IP. Даже с резидентным адресом такой поток выглядит подозрительно. Нужна ротация: каждые 100-200 запросов менять IP.
```python
from itertools import cycle
PROXIES = [
"http://user:pass@proxy1.lexic.ml:8080",
"http://user:pass@proxy2.lexic.ml:8080",
"http://user:pass@proxy3.lexic.ml:8080",
]
proxy_pool = cycle(PROXIES)
async def fetch_with_rotation(session, sku):
proxy = next(proxy_pool)
url = f"https://www.wildberries.ru/webapi/product/{sku}/info"
async with session.get(url, proxy=proxy) as resp:
return await resp.json()
```
Тут важно не менять IP на каждый запрос. Липкая сессия на 50-100 запросов — оптимальный вариант. Слишком частая смена тоже выглядит неестественно.
Заголовки, которые реально проверяются
Wildberries смотрит не только на стандартный набор. Есть специфические заголовки, которые выдают парсеры с головой.
`X-Client-Name` — должен быть `wbx`, иначе сервер вернёт ошибку. `X-Requested-With` — обязателен для AJAX-запросов. `Origin` — должен совпадать с доменом. Отсутствие любого из них — повод для подозрения.
Плюс проверяется `Accept-Encoding`. Если вы не поддерживаете gzip и brotli — вы бот. Браузеры всегда принимают сжатие, а скрипты часто забывают про этот заголовок.
Что делать при блокировке
Первое — стоп. Не долбите дальше, иначе получите бан по IP-диапазону. Подождите 10-15 минут, смените прокси.
Второе — проверьте cookie. Очистите сессию и начните заново. Часто блокировка цепляется именно к старой куке.
Третье — смените отпечаток TLS. Если используете requests — переходите на curl_cffi. Если уже на нём — попробуйте `impersonate="firefox"` вместо Chrome.
Итоговая схема
Рабочий парсер Wildberries выглядит так: резидентные прокси с липкой сессией, curl_cffi с имитацией Chrome, обработка cookie, случайные задержки 2-5 секунд, ротация IP каждые 100 запросов. Плюс мониторинг кодов ответа — если пошла серия 403, останавливаемся и меняем тактику.
Никакой магии. Только внимательность к деталям и понимание, как работает антибот. Обновляйте подход регулярно — Wildberries не спит, их инженеры тоже читают документацию.