Зачем парсить магазины на 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.jsonShopify — самый эффективный метод скрапинга. Используйте его до разбора HTML. - Единая архитектура парсера работает со всеми магазинами Shopify благодаря стандартизированной структуре.
- Резидентные прокси с ротацией обходят IP-лимиты Shopify.
- Используйте sticky-сессии при постраничном просмотре каталога одного магазина.
- Отслеживайте цены и наличие на уровне вариантов для комплексной конкурентной разведки.
- Начните с резидентных прокси ProxyHat для надёжного масштабирования скрапинга Shopify.
Готовы начать парсинг магазинов Shopify? Изучите наше руководство по скрапингу e-commerce данных для полной стратегии и ознакомьтесь с руководствами по использованию прокси в Python и использованию прокси в Node.js для деталей реализации. Посетите нашу страницу цен, чтобы начать.






