Search
Write a publication
Pull to refresh

Скрейпинг Temu в 2025: реальный кейс с антиботом, ротацией и прокси

Level of difficultyMedium
Reading time4 min
Views334

Разбираем полный цикл построения надёжного скрейпера для Temu: от выбора стека и прокси до обхода JavaScript‑челленджей и сбора тысяч карточек товаров без единого 403.

Проблематика и требования

  1. Антибот‑защита Temu

    • JS‑челленджи и динамические куки;

    • блокировка по повторяющимся заголовкам и шаблонному поведению;

    • гео‑таргетинг: часть контента недоступна вне целевых регионов.

  2. Цели скрейпинга

    • сбор названий, цен, рейтингов и ссылок на товары;

    • устойчивость к бану при сотнях параллельных сессий;

    • масштабируемость: легко добавить новые категории или регионы.

Технологический стек

  • Язык: Python 3.11+

  • Браузер: Playwright (Chromium) в headless‑режиме

  • Асинхронность: asyncio + встроенные возможности Playwright

  • Прокси: ProxyEmpire (ротация IP, порт 5000, аутентификация user:pass)

  • Парсинг: CSS‑селекторы; для сложных фрагментов — lxml/BeautifulSoup

Архитектура решения

[Список URL категорий] 
        ↓ (async dispatcher)
[Pool Playwright contexts] ← ProxyEmpire (ротация IP)
        ↓
[Рендеринг страниц → селекторы → парсинг]
        ↓
[Сохранение: CSV / PostgreSQL / MongoDB]
  • Playwright contexts изолируют куки и localStorage между задачами.

  • Ротация IP при создании каждого context: новый proxy-сервер.

  • Логирование всех ошибок и задержек для последующего анализа.

Конфигурация ProxyEmpire

# credentials.py
PROXY_USER = "ваш_логин"
PROXY_PASS = "ваш_пароль"
PROXY_HOST = "gateway.proxyempire.io"
PROXY_PORT = 5000
  • Пул из 5000 IP (США, ЕС, АЗИЯ и др.).

  • Аутентификация через HTTP Basic.

  • Round‑robin ротация на уровне TCP‑соединений Playwright.

Код: асинхронный скрейпер

# scraper.py
import asyncio
import logging
from playwright.async_api import async_playwright, TimeoutError as PlaywrightTimeout

from credentials import PROXY_USER, PROXY_PASS, PROXY_HOST, PROXY_PORT

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")


CATEGORY_URLS = [
    "https://www.temu.com/ru/computers-and-office-c-123.html",
    # добавьте другие разделы
]


async def fetch_category(page, url):
    try:
        await page.goto(url, timeout=60000)
        await page.wait_for_selector("div[class*='product-card']", timeout=10000)
        cards = await page.query_selector_all("div[class*='product-card']")
        results = []

        for card in cards:
            title = await card.query_selector_eval("h3", "el => el.innerText.trim()")
            price = await card.query_selector_eval("div[class*='price']", "el => el.innerText.trim()")
            link = await card.query_selector_eval("a", "el => el.href")
            results.append({"title": title, "price": price, "link": link})

        logging.info(f"[{url}] Собрано {len(results)} карточек")
        return results

    except PlaywrightTimeout as te:
        logging.warning(f"[{url}] Таймаут: {te}")
    except Exception as ex:
        logging.error(f"[{url}] Ошибка: {ex}")

    return []


async def worker(name, url_queue, output):
    proxy_url = f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        while not url_queue.empty():
            url = await url_queue.get()
            context = await browser.new_context(proxy={"server": proxy_url})
            page = await context.new_page()
            data = await fetch_category(page, url)
            output.extend(data)
            await context.close()
            await asyncio.sleep(1)   # необходимая задержка
            url_queue.task_done()
        await browser.close()


async def main():
    url_queue = asyncio.Queue()
    for url in CATEGORY_URLS:
        await url_queue.put(url)

    results = []
    workers = [asyncio.create_task(worker(f"worker-{i}", url_queue, results))
               for i in range(5)]  # 5 параллельных контекстов

    await url_queue.join()
    for w in workers:
        w.cancel()

    # Сохранение в CSV
    import csv
    with open("temu_products.csv", "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=["title", "price", "link"])
        writer.writeheader()
        writer.writerows(results)

    logging.info(f"Итого собрано: {len(results)} записей")


if __name__ == "__main__":
    asyncio.run(main())

Ключевые моменты реализации:

  • Queue + worker‑pattern для динамического распределения URL;

  • Изоляция контекстов Playwright для чистых сессий;

  • ProxyEmpire задаётся при создании context;

  • Обработка ошибок: таймауты и неожиданные исключения логируются и не прерывают работу.

Тонкости обхода защиты

  1. Динамические заголовки

    • Добавить ротацию User‑Agent: список популярных браузеров;

    • Маскировать головы: Accept-Language, Referer.

  2. Anti-Detect

    • Включить эмуляцию браузерных свойств:

context = await browser.new_context(
    proxy={...},
    user_agent=random.choice(USER_AGENTS),
    viewport={"width": 1280, "height": 720},
    java_script_enabled=True
)Куки и localStorage

Для части страниц требуется авторизация: сохранять и перезагружать state;

Captcha‑защита

Если появляются invisible reCAPTCHA, можно интегрировать антикапча-сервис.

3.Куки и localStorage

  • Для части страниц требуется авторизация: сохранять и перезагружать state;

4.Captcha‑защита

  • Если появляются invisible reCAPTCHA, можно интегрировать антикапча-сервис.

8. Выводы и рекомендации

  1. Headless‑браузер + прокси — оптимальный дуэт при сложных антибот‑механизмах.

  2. Изоляция сессий Playwright избавляет от “протекания” куки между задачами.

  3. Тонкая настройка User‑Agent, заголовков и задержек снижает подозрительное поведение.

  4. ProxyEmpire с портом 5000 обеспечивает качественную ротацию IP и гео‑таргетинг.

Полезные ссылки

Tags:
Hubs:
0
Comments0

Articles