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

от автора

Разбираем полный цикл построения надёжного скрейпера для 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 и гео‑таргетинг.

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


ссылка на оригинал статьи https://habr.com/ru/articles/934080/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *