Разбираем полный цикл построения надёжного скрейпера для Temu: от выбора стека и прокси до обхода JavaScript‑челленджей и сбора тысяч карточек товаров без единого 403.
Проблематика и требования
Антибот‑защита Temu
JS‑челленджи и динамические куки;
блокировка по повторяющимся заголовкам и шаблонному поведению;
гео‑таргетинг: часть контента недоступна вне целевых регионов.
Цели скрейпинга
сбор названий, цен, рейтингов и ссылок на товары;
устойчивость к бану при сотнях параллельных сессий;
масштабируемость: легко добавить новые категории или регионы.
Технологический стек
Язык: 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;
Обработка ошибок: таймауты и неожиданные исключения логируются и не прерывают работу.
Тонкости обхода защиты
Динамические заголовки
Добавить ротацию User‑Agent: список популярных браузеров;
Маскировать головы:
Accept-Language
,Referer
.
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. Выводы и рекомендации
Headless‑браузер + прокси — оптимальный дуэт при сложных антибот‑механизмах.
Изоляция сессий Playwright избавляет от “протекания” куки между задачами.
Тонкая настройка User‑Agent, заголовков и задержек снижает подозрительное поведение.
ProxyEmpire с портом 5000 обеспечивает качественную ротацию IP и гео‑таргетинг.
Полезные ссылки
Playwright Python: https://playwright.dev/python/
ProxyEmpire: https://proxyempire.io/
Документация Temu (для разработчиков): https://www.temu.com/