Как скрапить Etsy: руководство по нишевым исследованиям для POD

Прагматичный гайд по скрапингу Etsy для POD-команд: структура сайта, обход Cloudflare, residential-прокси, Python-примеры и этика работы с малым бизнесом.

Как скрапить Etsy: руководство по нишевым исследованиям для POD
В этой статье

Скрапинг Etsy: API или HTML — с чего начать

Etsy предоставляет Open API v3, но он спроектирован для seller-tools и требует OAuth. Эндпоинты вроде /v3/application/shops и /v3/application/listings доступны только через приложение, одобренное Etsy. Для нишевых исследований — поиска трендов, анализа конкурентов, оценки цен — HTML-поверхность остаётся единственным реальным вариантом. API не отдаёт данные о продажах магазинов, не показывает трендовые поисковые запросы и не даёт доступа к дереву категорий. В этом руководстве — структура Etsy, защита от ботов, конкретные паттерны скрапинга и рабочие примеры на Python.

Структура Etsy: что и как скрапить

Страницы поиска

URL поиска: https://www.etsy.com/search?q=mug+design. Результаты загружаются серверным рендерингом — карточки товаров доступны в HTML без JavaScript. Пагинация — параметр &page=2 в URL. Etsy показывает до ~500 страниц результатов, но для нишевых исследований обычно достаточно первых 5–10.

Ключевые CSS-селекторы для страницы поиска:

  • Контейнер результатов: div[data-results]
  • Карточки товаров: div[data-results] .v2-listing-card или [data-listing-id]
  • Название: .v2-listing-card__info h3
  • Цена: .currency-value
  • Рейтинг: .v2-listing-card__info .star-rating
  • Ссылка на товар: a.listing-link (атрибут href)
  • Общее число результатов: .wt-text-gray .wt-mb-xs-0 (текст «X,XXX results»)

Страницы товаров (листинги)

URL листинга: https://www.etsy.com/listing/123456789/item-name. Etsy рендерит ключевые данные через JSON в теге <script type="application/ld+json"> — это самый надёжный способ извлечения структурированных данных, не зависящий от CSS-классов.

Что извлекаем из листинга:

  • Заголовок, описание, цена, валюта: из ld+json блока (@type: Product)
  • Продавец: a[data-shop-id] — имя и ID магазина
  • Количество продаж продавца: текст бейджа «X sales» рядом с именем
  • Изображения: img[data-src-zoom]
  • Варианты: [data-variation-id]

Страницы магазинов

URL магазина: https://www.etsy.com/shop/ShopName. Etsy публично показывает бейдж «X sales» — это приблизительное число продаж с точностью ±10%. Количество листингов — через пагинацию: /shop/ShopName?section_id=0&page=2. Отзывы: /shop/ShopName/reviews.

Дерево категорий

Etsy организует товары в иерархию: https://www.etsy.com/c/clothing, /c/clothing/womens-clothing и так далее. Навигационное меню содержит ссылки на все верхние категории. Подкатегории извлекаются рекурсивно через ссылки на страницах категорий — селектор a[href*="/c/"]. Для POD-исследований категории — отправная точка: от них можно идти вниз до конкретных подкатегорий и оценивать конкуренцию.

Антибот Etsy: Cloudflare и лимиты запросов

Etsy использует двухуровневую защиту:

  1. Cloudflare WAF — JavaScript-челленджи и rate-limiting на уровне CDN. При подозрении Cloudflare показывает interstitial-страницу или 403. Residential-прокси значительно снижают вероятность челленджа, потому что IP выглядит как реальный пользователь из домашней сети.
  2. Внутренние лимиты Etsy — отслеживание частоты запросов с одного IP. Порог: примерно 100–200 запросов за 10–15 минут до появления 429 Too Many Requests. Лимиты строже для страниц поиска, чем для страниц отдельных листингов.

Практические правила:

  • Держите 1–2 запроса в секунду на один IP.
  • Ротируйте IP каждые 50–80 запросов.
  • Используйте residential-прокси — datacenter-IP Etsy блокирует агрессивнее.
  • Добавляйте реалистичные заголовки: User-Agent, Accept, Accept-Language, Referer.
  • Не пытайтесь программно обходить Cloudflare-челлендж — это эскалирует блокировку до банлиста.

Выбор типа прокси для Etsy

Тип проксиОбход CloudflareСкоростьРекомендация для Etsy
ResidentialОтличноСредняя (1–3 с)Лучший выбор — IP реальных пользователей
MobileОтличноНиже (2–5 с)Хорошо для sticky-сессий
DatacenterПлохоВысокая (<0.5 с)Только для невысоких объёмов

Для нишевых исследований на Etsy мы рекомендуем residential-прокси с ротацией по запросам. Настройка ProxyHat:

# HTTP residential proxy — ротация IP по каждому запросу
http://user-country-US:YOUR_PASSWORD@gate.proxyhat.com:8080

# Sticky-сессия для многостраничного скрапинга магазина
http://user-session-shop1:YOUR_PASSWORD@gate.proxyhat.com:8080

# Городское таргетирование — IP из конкретного города
http://user-country-US-city-new_york:YOUR_PASSWORD@gate.proxyhat.com:8080

Подробнее о доступных локациях — на странице ProxyHat Locations.

Паттерны скрапинга для поиска ниш

Трендовые поисковые запросы

Etsy не показывает трендовые запросы напрямую, но подстрока автодополнения доступна по адресу:

# Пример: автодополнения для запроса "mug"
curl -x http://user-country-US:YOUR_PASSWORD@gate.proxyhat.com:8080 \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)" \
  -H "Accept: application/json" \
  "https://www.etsy.com/api/v3/ajax/member/search-suggestions?query=mug"

Ответ — JSON с массивом предложений. Пример усечённого ответа:

{
  "results": [
    {"query": "mug", "display_name": "mug"},
    {"query": "mug rug", "display_name": "mug rug"},
    {"query": "mug holder", "display_name": "mug holder"},
    {"query": "mug cozy", "display_name": "mug cozy"},
    {"query": "mug tree", "display_name": "mug tree"},
    {"query": "mug personalized", "display_name": "mug personalized"}
  ]
}

Собирая подсказки для сотен seed-запросов, вы получаете карту спроса в категории. Для POD это особенно ценно: подсказки вроде «mug cozy» или «mug rug» указывают на субниши с меньшей конкуренцией.

Количество продавцов в нише

На странице поиска Etsy показывает «X,XXX results» — это общее число товаров, а не продавцов. Но каждый товар привязан к магазину. Собирая уникальные shop_id со страниц поиска, вы получаете оценку конкуренции. Алгоритм: скрапить 5–10 страниц результатов → извлечь data-shop-id из каждой карточки → посчитать уникальные значения.

Средние цены и ценовые сегменты

Извлекайте цены из карточек поиска и вычисляйте медиану, 25-й и 75-й перцентили. Это показывает, что покупатели готовы платить в данной нише — критически важно для POD-ценообразования. Если медианная цена кружки — $16, а вы планируете продавать за $30, это сигнал к пересмотру дизайна или позиционирования.

Полный цикл нишевого анализа

  1. Seed-запросы: соберите 50–100 ключевых слов через автодополнение.
  2. Объём результатов: для каждого запроса получите «X results» со страницы поиска.
  3. Конкуренция: извлеките уникальные shop_id — сколько продавцов в нише.
  4. Цены: соберите цены, вычислите статистику.
  5. Анализ лидеров: топ-10 магазинов по продажам — что они продают, как оформляют.
  6. Решение: ниша с <500 продавцов, медианная цена >$15 и растущим числом результатов — перспективна для POD.

Скрапинг поиска Etsy на Python

Ниже — полный пример: запрос страницы поиска → парсинг карточек → сбор данных для нишевого анализа.

import requests
from bs4 import BeautifulSoup
import re
import time
import random
import json

PROXY_USER = "user-country-US"
PROXY_PASS = "YOUR_PASSWORD"
PROXY_URL = f"http://{PROXY_USER}:{PROXY_PASS}@gate.proxyhat.com:8080"
PROXIES = {"http": PROXY_URL, "https": PROXY_URL}

HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/125.0.0.0 Safari/537.36"
    ),
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Referer": "https://www.etsy.com/",
}

def fetch_search(query, page=1):
    """Получить страницу поиска Etsy."""
    url = f"https://www.etsy.com/search?q={query}&page={page}"
    resp = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=15)
    resp.raise_for_status()
    return resp.text

def parse_search_results(html):
    """Извлечь данные из карточек товаров."""
    soup = BeautifulSoup(html, "html.parser")
    listings = []

    cards = soup.select("div[data-results] .v2-listing-card")
    if not cards:
        cards = soup.select("[data-listing-id]")

    for card in cards:
        link = card.select_one("a.listing-link")
        title_el = card.select_one("h3")
        price_el = card.select_one(".currency-value")
        shop_el = card.select_one("[data-shop-id]")

        listing = {
            "title": title_el.get_text(strip=True) if title_el else None,
            "url": link["href"] if link and link.get("href") else None,
            "price": price_el.get_text(strip=True) if price_el else None,
            "listing_id": card.get("data-listing-id"),
            "shop_id": shop_el.get("data-shop-id") if shop_el else None,
        }
        if listing["url"]:
            listings.append(listing)

    # Общее число результатов
    results_text = soup.select_one(".wt-text-gray")
    total = None
    if results_text:
        match = re.search(r"([\d,]+)\s+results", results_text.get_text())
        if match:
            total = int(match.group(1).replace(",", ""))

    return {"listings": listings, "total_results": total}

# Собираем данные по нише
niche_data = []
for page in range(1, 6):
    html = fetch_search("custom+mug", page=page)
    result = parse_search_results(html)
    niche_data.extend(result["listings"])
    print(f"Page {page}: {len(result['listings'])} listings, "
          f"total: {result['total_results']}")
    time.sleep(random.uniform(2.0, 4.0))

Детальные страницы товаров с ротацией прокси

При переходе к страницам товаров важно ротировать IP, чтобы не упереться в лимиты. ProxyHat позволяет управлять ротацией через параметр session в имени пользователя — каждый новый session_id = новый IP.

def fetch_listing_detail(listing_url, session_id):
    """Получить детальную страницу товара с ротацией прокси."""
    proxy = f"http://user-session-{session_id}:{PROXY_PASS}@gate.proxyhat.com:8080"
    proxies = {"http": proxy, "https": proxy}

    resp = requests.get(listing_url, headers=HEADERS, proxies=proxies, timeout=15)
    resp.raise_for_status()
    return resp.text

def extract_listing_data(html):
    """Извлечь структурированные данные из ld+json блока."""
    soup = BeautifulSoup(html, "html.parser")
    data = {}

    for script in soup.find_all("script", type="application/ld+json"):
        try:
            ld = json.loads(script.string)
            if ld.get("@type") == "Product":
                data["name"] = ld.get("name")
                offers = ld.get("offers", {})
                data["price"] = offers.get("price")
                data["currency"] = offers.get("priceCurrency")
                data["description"] = (ld.get("description") or "")[:500]
        except (json.JSONDecodeError, TypeError):
            continue

    # Бейдж продаж продавца
    sales_el = soup.select_one("[data-sales-count]")
    if not sales_el:
        # Fallback: поиск по тексту
        for el in soup.select(".wt-text-gray"):
            match = re.search(r"(\d[\d,]*)\s+sales?", el.get_text())
            if match:
                data["seller_sales"] = int(match.group(1).replace(",", ""))
                break

    # ID и имя магазина
    shop_link = soup.select_one("a[data-shop-id]")
    if shop_link:
        data["shop_id"] = shop_link.get("data-shop-id")
        data["shop_name"] = shop_link.get_text(strip=True)

    return data

# Собираем детальные данные по листингам
all_details = []
for i, listing in enumerate(niche_data[:25]):
    session = f"etsy{i:04d}"
    try:
        detail_html = fetch_listing_detail(listing["url"], session)
        detail = extract_listing_data(detail_html)
        detail["search_rank"] = i + 1
        all_details.append(detail)
        print(f"{i+1}. {detail.get('name', 'N/A')[:60]} — "
              f"{detail.get('price', '?')} {detail.get('currency', '')}")
    except requests.HTTPError as e:
        print(f"{i+1}. HTTP error: {e.response.status_code}")
    time.sleep(random.uniform(1.5, 3.0))

Аналитика магазинов Etsy

Страница магазина — ключ к пониманию конкуренции. Etsy публично раскрывает:

  • Бейдж «X sales» — примерное количество продаж. Для нишевых исследований этого достаточно: сравнивайте продавцов между собой.
  • Количество листингов — доступно через пагинацию или атрибут data-listings-count.
  • Рейтинг и отзывы/shop/ShopName/reviews. Рейтинг и тексты отзывов дают понимание, что ценят покупатели.
def analyze_shop(shop_name):
    """Собрать базовую аналитику магазина Etsy."""
    url = f"https://www.etsy.com/shop/{shop_name}"
    proxy = f"http://user-country-US:{PROXY_PASS}@gate.proxyhat.com:8080"
    proxies = {"http": proxy, "https": proxy}

    resp = requests.get(url, headers=HEADERS, proxies=proxies, timeout=15)
    soup = BeautifulSoup(resp.text, "html.parser")
    shop_data = {"name": shop_name}

    # Количество продаж — бейдж «X sales»
    for el in soup.select(".wt-text-gray"):
        match = re.search(r"(\d[\d,]*)\s+sales?", el.get_text())
        if match:
            shop_data["sales_count"] = int(match.group(1).replace(",", ""))
            break

    # Количество листингов
    listing_count_el = soup.select_one("[data-listings-count]")
    if listing_count_el:
        shop_data["listing_count"] = int(
            listing_count_el["data-listings-count"]
        )

    # Рейтинг магазина
    rating_el = soup.select_one("[data-rating]")
    if rating_el:
        shop_data["rating"] = float(rating_el["data-rating"])

    return shop_data

# Анализируем уникальные магазины из ниши
unique_shops = set()
for detail in all_details:
    if detail.get("shop_name"):
        unique_shops.add(detail["shop_name"])

shop_analytics = []
for shop in list(unique_shops)[:10]:
    data = analyze_shop(shop)
    shop_analytics.append(data)
    print(f"{shop}: {data.get('sales_count', '?')} sales, "
          f"{data.get('listing_count', '?')} listings, "
          f"rating: {data.get('rating', '?')}")
    time.sleep(random.uniform(2.0, 4.0))

# Сводная статистика по нише
import statistics
prices = [float(d["price"]) for d in all_details if d.get("price")]
sales = [d["seller_sales"] for d in all_details if d.get("seller_sales")]
print(f"\nНишевая статистика:")
print(f"  Медианная цена: ${statistics.median(prices):.2f}")
print(f"  25-й перцентиль: ${statistics.quantiles(prices, n=4)[0]:.2f}")
print(f"  75-й перцентиль: ${statistics.quantiles(prices, n=4)[2]:.2f}")
print(f"  Средние продажи продавца: {statistics.mean(sales):.0f}")
print(f"  Уникальных магазинов: {len(unique_shops)}")

Этика: продавцы Etsy — малый бизнес

Etsy — площадка независимых продавцов. Многие из них — мастера, создающие уникальные товары ручной работы. При скрапинге соблюдайте принципы:

  • Исследуйте, не копируйте. Скрапинг для анализа трендов и цен — нормально. Копирование дизайнов, фотографий и описаний — нарушение авторских прав и прямой вред малому бизнесу.
  • Соблюдайте robots.txt. Etsy разрешает скрапинг определённых путей — проверяйте https://www.etsy.com/robots.txt перед запуском. Если путь запрещён — не скрапите его.
  • Не перегружайте серверы. Держите скорость ≤2 запросов/сек с ротацией IP. Агрессивный скрапинг замедляет сайт для реальных покупателей и продавцов.
  • Уважайте авторские права. Дизайны, фотографии и тексты защищены. Используйте данные для исследований рынка, а не для воспроизведения чужого труда.
  • GDPR/CCPA. Не собирайте персональные данные продавцов сверх необходимого для аналитики. Имя магазина и количество продаж — достаточно.

Скрапинг Etsy для нишевых исследований — инструмент, а не оружие. Цель — понять рынок и найти свою нишу, а не обмануть продавцов.

Подробнее об этике и лучших практиках скрапинга — в нашем материале о веб-скрапинге и этике.

Ключевые выводы

  • Etsy Open API не подходит для нишевых исследований — скрапинг HTML остаётся основным подходом.
  • Residential-прокси обязательны: Cloudflare и внутренние лимиты Etsy быстро блокируют datacenter-IP.
  • Держите 1–2 запроса/сек, ротируйте IP каждые 50–80 запросов через ProxyHat.
  • Структурированные данные в ld+json — самый надёжный источник на страницах товаров.
  • Бейдж «X sales» и количество листингов — ключевые метрики для оценки конкуренции в нише.
  • API автодополнений — быстрый способ собрать seed-запросы для нишевого анализа.
  • Скрапите для исследований, не для копирования. Уважайте труд независимых продавцов.

Часто задаваемые вопросы

Как скрапить Etsy без блокировки?

Используйте residential-прокси, держите скорость 1–2 запроса/сек, ротируйте IP каждые 50–80 запросов и добавляйте реалистичные HTTP-заголовки (User-Agent, Accept, Referer). Не пытайтесь обходить Cloudflare-челлендж программно — это усиливает блокировку.

Какие прокси лучше для скрапинга Etsy?

Residential-прокси — лучший выбор. Etsy использует Cloudflare, который агрессивно блокирует datacenter-IP. Residential-IP выглядят как реальные пользователи, что снижает вероятность челленджа. Mobile-прокси тоже работают, но медленнее. Datacenter-прокси подходят только для малых объёмов.

Легально ли скрапить Etsy?

Скрапинг публично доступных данных для личных исследований обычно законен, но зависит от юрисдикции. Соблюдайте robots.txt, не копируйте контент, не перегружайте серверы и не собирайте персональные данные. Копирование дизайнов и описаний нарушает авторские права.

Как найти прибыльную нишу на Etsy для POD?

Соберите seed-запросы через API автодополнений Etsy, скрапьте страницы поиска для оценки числа результатов и конкуренции, извлеките цены и количество продаж продавцов. Перспективная ниша: менее 500 продавцов, медианная цена выше $15, растущий спрос.

Подходит ли Etsy Open API для нишевых исследований?

Нет. Etsy Open API v3 требует OAuth и предназначен для seller-tools. Он не отдаёт данные о продажах магазинов, трендовые запросы и дерево категорий. Для нишевых исследований нужен скрапинг HTML-страниц.

Готовы начать?

Резидентные, ISP и мобильные прокси в 148+ странах. Создайте бесплатный аккаунт.

Создать бесплатный аккаунт
← Вернуться в Блог