Как парсить магазины Shopify с помощью прокси: полное руководство

Узнайте, как парсить данные магазинов Shopify через JSON API эндпоинты и резидентные прокси. Полные примеры кода на Python и Node.js для извлечения товаров, цен и данных о наличии.

Как парсить магазины Shopify с помощью прокси: полное руководство

Зачем парсить магазины на Shopify?

Shopify обеспечивает работу более 4 миллионов интернет-магазинов по всему миру, от небольших независимых брендов до крупных ритейлеров. Это делает платформу одним из богатейших источников e-commerce аналитики. Парся магазины Shopify, вы можете отслеживать цены конкурентов, мониторить запуск новых продуктов, анализировать рыночные тенденции и создавать комплексные базы данных товаров.

Хорошая новость в том, что Shopify имеет предсказуемую структуру, что делает скрапинг более системным, чем на большинстве e-commerce платформ. Каждый магазин Shopify предоставляет определённые данные через стандартизированные эндпоинты, поэтому единая архитектура парсера может работать с тысячами разных магазинов. Для более широкого обзора стратегий e-commerce скрапинга смотрите наше руководство по скрапингу e-commerce данных.

Структура магазина Shopify

Каждый магазин Shopify следует одним и тем же паттернам URL и данных, независимо от темы или кастомизации.

Публичные JSON-эндпоинты

Shopify предоставляет данные о товарах через JSON-эндпоинты, не требующие аутентификации. Это самый эффективный способ парсинга магазинов Shopify, так как вы получаете структурированные данные без разбора HTML.

ЭндпоинтВозвращаемые данныеПагинация
/products.jsonВсе товары с вариантами, ценами, изображениями?page=N&limit=250
/products/{handle}.jsonДетали одного товараНет
/collections.jsonВсе коллекции?page=N
/collections/{handle}/products.jsonТовары в коллекции?page=N&limit=250
/meta.jsonМетаданные магазина (название, описание)Нет

Структура данных товара

Каждый объект товара из JSON API включает:

  • Базовая информация: title, handle (слаг), body_html (описание), vendor, product_type, tags
  • Варианты: каждый вариант имеет свою цену, compare_at_price, SKU, статус наличия и значения опций (размер, цвет и т.д.)
  • Изображения: URL всех изображений товара с alt-текстом
  • Даты: created_at, updated_at, published_at

Ограничение частоты запросов

Shopify применяет лимиты частоты запросов для защиты производительности магазинов. Публичные JSON-эндпоинты обычно позволяют 2-4 запроса в секунду на один IP до срабатывания ограничений. Именно здесь резидентные прокси становятся необходимы — распределение запросов по нескольким IP позволяет поддерживать пропускную способность без превышения лимитов на отдельном IP.

Настройка прокси для Shopify

Ограничение частоты Shopify основано на IP, что делает ротацию прокси основной стратегией для масштабного скрапинга.

Настройка ProxyHat

# Ротирующий резидентный прокси (новый IP при каждом запросе)
http://USERNAME:PASSWORD@gate.proxyhat.com:8080
# Геотаргетинг для магазинов определённого региона
http://USERNAME-country-US:PASSWORD@gate.proxyhat.com:8080
# Sticky-сессия для постраничного парсинга одного магазина
http://USERNAME-session-shopify001:PASSWORD@gate.proxyhat.com:8080

Для скрапинга Shopify используйте ротацию при каждом запросе при парсинге разных магазинов, и sticky-сессии при постраничном просмотре каталога одного магазина. Этот паттерн имитирует естественное поведение пользователя.

Реализация на Python

Вот готовый к производству парсер Shopify с использованием Python SDK от ProxyHat.

Парсер JSON API

import requests
import json
import time
import random
from dataclasses import dataclass, field
from typing import Optional
PROXY_URL = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
]
@dataclass
class ShopifyProduct:
    id: int
    title: str
    handle: str
    vendor: str
    product_type: str
    tags: list[str]
    variants: list[dict]
    images: list[str]
    min_price: float
    max_price: float
    created_at: str
    updated_at: str
def get_session(store_domain: str) -> requests.Session:
    """Create a session with proxy and headers configured."""
    session = requests.Session()
    session.proxies = {"http": PROXY_URL, "https": PROXY_URL}
    session.headers.update({
        "User-Agent": random.choice(USER_AGENTS),
        "Accept": "application/json",
        "Accept-Language": "en-US,en;q=0.9",
    })
    return session
def scrape_all_products(store_domain: str) -> list[ShopifyProduct]:
    """Scrape all products from a Shopify store via JSON API."""
    products = []
    page = 1
    session = get_session(store_domain)
    while True:
        url = f"https://{store_domain}/products.json?page={page}&limit=250"
        try:
            response = session.get(url, timeout=30)
            response.raise_for_status()
        except requests.RequestException as e:
            print(f"Error on page {page}: {e}")
            break
        data = response.json()
        page_products = data.get("products", [])
        if not page_products:
            break
        for p in page_products:
            prices = [float(v["price"]) for v in p.get("variants", [])
                      if v.get("price")]
            product = ShopifyProduct(
                id=p["id"],
                title=p["title"],
                handle=p["handle"],
                vendor=p.get("vendor", ""),
                product_type=p.get("product_type", ""),
                tags=p.get("tags", "").split(", ") if p.get("tags") else [],
                variants=[{
                    "id": v["id"],
                    "title": v["title"],
                    "price": v["price"],
                    "compare_at_price": v.get("compare_at_price"),
                    "sku": v.get("sku"),
                    "available": v.get("available", False),
                } for v in p.get("variants", [])],
                images=[img["src"] for img in p.get("images", [])],
                min_price=min(prices) if prices else 0,
                max_price=max(prices) if prices else 0,
                created_at=p.get("created_at", ""),
                updated_at=p.get("updated_at", ""),
            )
            products.append(product)
        print(f"Page {page}: {len(page_products)} products (total: {len(products)})")
        page += 1
        time.sleep(random.uniform(1, 3))
    return products
def scrape_collections(store_domain: str) -> list[dict]:
    """Scrape all collections from a Shopify store."""
    collections = []
    page = 1
    session = get_session(store_domain)
    while True:
        url = f"https://{store_domain}/collections.json?page={page}"
        try:
            response = session.get(url, timeout=30)
            response.raise_for_status()
        except requests.RequestException:
            break
        data = response.json()
        page_collections = data.get("collections", [])
        if not page_collections:
            break
        collections.extend(page_collections)
        page += 1
        time.sleep(random.uniform(1, 2))
    return collections
# Пример: парсинг нескольких магазинов Shopify
if __name__ == "__main__":
    stores = [
        "example-store-1.myshopify.com",
        "example-store-2.com",
        "example-store-3.com",
    ]
    for store in stores:
        print(f"\nScraping: {store}")
        products = scrape_all_products(store)
        print(f"Found {len(products)} products")
        with open(f"{store.replace('.', '_')}_products.json", "w") as f:
            json.dump([vars(p) for p in products], f, indent=2)
        time.sleep(random.uniform(3, 7))

Мониторинг изменений цен по магазинам

def compare_prices(store_domain: str, previous_data: dict) -> list[dict]:
    """Compare current prices with previously stored data."""
    changes = []
    products = scrape_all_products(store_domain)
    for product in products:
        prev = previous_data.get(product.handle)
        if not prev:
            changes.append({
                "type": "new_product",
                "handle": product.handle,
                "title": product.title,
                "price": product.min_price,
            })
            continue
        if product.min_price != prev.get("min_price"):
            changes.append({
                "type": "price_change",
                "handle": product.handle,
                "title": product.title,
                "old_price": prev["min_price"],
                "new_price": product.min_price,
                "change_pct": ((product.min_price - prev["min_price"])
                               / prev["min_price"] * 100)
                              if prev["min_price"] else 0,
            })
    return changes

Реализация на Node.js

Версия на Node.js с использованием Node SDK от ProxyHat.

const axios = require("axios");
const { HttpsProxyAgent } = require("https-proxy-agent");
const fs = require("fs");
const PROXY_URL = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080";
const agent = new HttpsProxyAgent(PROXY_URL);
async function scrapeShopifyProducts(storeDomain) {
  const products = [];
  let page = 1;
  while (true) {
    const url = `https://${storeDomain}/products.json?page=${page}&limit=250`;
    try {
      const { data } = await axios.get(url, {
        httpsAgent: agent,
        headers: {
          "User-Agent":
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
          Accept: "application/json",
        },
        timeout: 30000,
      });
      const pageProducts = data.products || [];
      if (pageProducts.length === 0) break;
      for (const p of pageProducts) {
        const prices = p.variants.map((v) => parseFloat(v.price)).filter(Boolean);
        products.push({
          id: p.id,
          title: p.title,
          handle: p.handle,
          vendor: p.vendor,
          productType: p.product_type,
          tags: p.tags ? p.tags.split(", ") : [],
          minPrice: Math.min(...prices),
          maxPrice: Math.max(...prices),
          variants: p.variants.map((v) => ({
            id: v.id,
            title: v.title,
            price: v.price,
            compareAtPrice: v.compare_at_price,
            sku: v.sku,
            available: v.available,
          })),
          images: p.images.map((img) => img.src),
          updatedAt: p.updated_at,
        });
      }
      console.log(`Page ${page}: ${pageProducts.length} products (total: ${products.length})`);
      page++;
      await new Promise((r) => setTimeout(r, 1000 + Math.random() * 2000));
    } catch (err) {
      console.error(`Error on page ${page}: ${err.message}`);
      break;
    }
  }
  return products;
}
// Использование
scrapeShopifyProducts("example-store.com").then((products) => {
  fs.writeFileSync("shopify_data.json", JSON.stringify(products, null, 2));
  console.log(`Saved ${products.length} products`);
});

Стратегии скрапинга, специфичные для Shopify

Обнаружение магазинов на Shopify

Прежде чем парсить, нужно определить, какие сайты конкурентов работают на Shopify. Типичные признаки:

  • Эндпоинт /products.json возвращает валидный JSON
  • HTML-код содержит Shopify.theme или cdn.shopify.com
  • Заголовок x-shopify-stage присутствует в ответах

Обработка закрытых магазинов

Некоторые магазины Shopify требуют пароль для доступа. Обычно это магазины перед запуском или оптовые магазины. JSON-эндпоинты вернут редирект на страницу ввода пароля. Пропускайте такие магазины в пайплайне скрапинга, если у вас нет авторизованного доступа.

Работа с пользовательскими доменами

Магазины Shopify часто используют пользовательские домены вместо .myshopify.com. JSON API работает одинаково на пользовательских доменах. Просто используйте публичный домен магазина в запросах.

Отслеживание запасов

Варианты товаров включают поле available, указывающее на наличие товара. Отслеживая это поле со временем, вы можете мониторить уровень запасов конкурентов и определять, когда товары заканчиваются — полезная информация для решений по ценообразованию и пополнению запасов.

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

Хотя Shopify более дружественен к скраперам, чем Amazon, он всё же применяет защитные механизмы.

ЗащитаДеталиСпособ обхода
IP-лимиты~2-4 запроса/сек на IP для JSON-эндпоинтовРотация резидентных прокси между запросами
Защита CloudflareНекоторые магазины используют CloudflareРезидентные IP с браузерными заголовками
Обнаружение ботовМониторинг поведенческих паттерновРандомизация задержек и User-Agent
Страницы с паролемМагазины перед запуском/оптовые закрытыПропуск или авторизованный доступ

Подробнее об обработке антибот-систем читайте в нашем руководстве о том, как скрапить сайты без блокировок.

Ключевой вывод: JSON API Shopify — самый эффективный подход к скрапингу: вы получаете структурированные данные без разбора HTML. Используйте его в первую очередь, прежде чем прибегать к HTML-скрапингу.

Сценарии использования данных

После сбора данных товаров Shopify вот наиболее ценные применения:

  • Конкурентное ценообразование: отслеживайте цены конкурентов по категориям товаров и корректируйте свою ценовую стратегию в реальном времени.
  • Исследование продуктов: выявляйте трендовые товары, новые запуски и пробелы на рынке, мониторя множество магазинов.
  • Анализ рынка: агрегируйте данные из сотен магазинов Shopify для понимания рыночных тенденций, распределения цен и роста категорий.
  • Обогащение каталога: используйте описания, изображения и характеристики товаров конкурентов для улучшения собственных листингов.
  • Мониторинг бренда: отслеживайте несанкционированных продавцов ваших товаров и мониторьте соблюдение MAP на витринах Shopify.

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

  • Эндпоинт /products.json Shopify — самый эффективный метод скрапинга. Используйте его до разбора HTML.
  • Единая архитектура парсера работает со всеми магазинами Shopify благодаря стандартизированной структуре.
  • Резидентные прокси с ротацией обходят IP-лимиты Shopify.
  • Используйте sticky-сессии при постраничном просмотре каталога одного магазина.
  • Отслеживайте цены и наличие на уровне вариантов для комплексной конкурентной разведки.
  • Начните с резидентных прокси ProxyHat для надёжного масштабирования скрапинга Shopify.

Готовы начать парсинг магазинов Shopify? Изучите наше руководство по скрапингу e-commerce данных для полной стратегии и ознакомьтесь с руководствами по использованию прокси в Python и использованию прокси в Node.js для деталей реализации. Посетите нашу страницу цен, чтобы начать.

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

Доступ к более чем 50 млн резидентных IP в 148+ странах с AI-фильтрацией.

Смотреть ценыРезидентные прокси
← Вернуться в Блог