Скрапинг Shopify products.json: конечная точка, которую открывает каждый магазин
Забудьте про парсинг HTML. Каждый магазин на Shopify предоставляет вам чистый JSON на /products.json — полный каталог, цены, варианты, наличие. Вот ограничения пагинации, ускоряющие трюки и как превратить это в мониторинг конкурентов.
Любой, кто занимался скрапингом электронной коммерции, знает эту боль: хрупкие CSS-селекторы, вложенные div-обертки, макеты, которые меняются и ломают всё за ночь. Shopify предлагает вам выход. Почти каждый магазин на Shopify открывает публичную конечную точку /products.json, которая возвращает весь каталог в виде чистого, структурированного JSON — заголовки, идентификаторы, поставщики, теги, варианты, цены, флаги наличия и изображения. Никакого парсинга HTML, никакого безголового браузера. Это руководство охватывает, как работает конечная точка, её реальные ограничения пагинации, трюк, который значительно ускоряет сканирование больших магазинов, и как превратить всё это в мониторинг цен и наличия у конкурентов.
Конечная точка в каждом магазине Shopify
Добавьте /products.json к корневому домену любого магазина Shopify, и вы получите JSON-список продуктов. Это те же данные, которые использует витрина, открытые по дизайну для Ajax API. Каждый продукт имеет стабильный числовой id, handle, vendor, product_type, tags, массив variants (каждый с собственной ценой, SKU и логическим available), и изображения. Это всё, что вы обычно скрапите с продуктовой страницы, доставляется в одном запросе.
import requests
url = "https://store.example.com/products.json"
r = requests.get(url, params={"limit": 250, "page": 1}, timeout=15)
products = r.json()["products"]
print(len(products), products[0]["title"])
for v in products[0]["variants"]:
print(v["sku"], v["price"], v["available"])
Два предостережения сразу. Shopify ограничивает публичную конечную точку до 250 продуктов на страницу — это ограничение, которое вы не можете превысить, независимо от того, что вы передаете, поэтому игнорируйте советы установить limit=1000000 и захватить всё в одном вызове. И меньшинство магазинов отключает конечную точку (иногда пользовательские сборки её отключают), поэтому проверьте на валидный ответ, прежде чем строить вокруг неё конвейер.
Пагинация до конца
Из-за ограничения в 250, полный каталог означает переход по страницам, пока вы не наткнетесь на пустую. Наивный цикл увеличивает page и останавливается, когда страница возвращается пустой. Это правильно и подходит для небольших магазинов — магазин с несколькими сотнями продуктов требует пару запросов.
def all_products(base):
page, out = 1, []
while True:
r = requests.get(f"{base}/products.json",
params={"limit": 250, "page": page}, timeout=15)
batch = r.json()["products"]
if not batch:
break
out.extend(batch)
page += 1
return out

Трюк 25x: сначала найдите последнюю страницу
Последовательная пагинация медленная в больших магазинах, потому что вы не можете параллелизировать — вы не знаете, когда остановиться, поэтому каждая страница ждёт предыдущую. Решение — бинарный поиск: исследуйте номера страниц, чтобы найти последнюю непустую страницу, затем отправьте все запросы страниц одновременно. В публичном тесте против магазина с 25,000 продуктов на 833 страницах это сократило сканирование с примерно 120 секунд до около 12 на первом запуске и около 5, когда номер последней страницы был закэширован — 25-кратное ускорение. Направьте запросы через вращающиеся IP, чтобы всплеск одновременных запросов не вызвал ограничение.
PROXY = "http://USER:PASS@gate.quantumproxies.io:8000"
proxies = {"http": PROXY, "https": PROXY}
def last_page(base, hi=1000):
lo, last = 1, 1
while lo <= hi:
mid = (lo + hi) // 2
r = requests.get(f"{base}/products.json",
params={"limit": 250, "page": mid},
proxies=proxies, timeout=15)
if r.json()["products"]:
last, lo = mid, mid + 1 # go higher
else:
hi = mid - 1 # go lower
return last
# then request pages 1..last_page concurrently
Как сказал Роб Пайк, сложные алгоритмы медленные, когда n мал — для магазина с 200 продуктами пропустите бинарный поиск и просто используйте цикл. Он окупается на каталогах в тысячи, где последовательная пагинация затягивает.
Сузьте выборку: коллекции и отдельные продукты
Вам не всегда нужен весь каталог. Shopify предоставляет тот же JSON на уровне коллекции: /collections/<handle>/products.json возвращает только продукты в этой коллекции, с той же пагинацией. Это эффективный путь, когда вы отслеживаете только одну категорию — кроссовки, ароматы, один бренд — а не весь магазин. А чтобы изучить один продукт, добавьте .json к его URL: /products/<handle>.json даст полную запись этого продукта, включая каждый вариант и его флаг наличия, что удобно для плотного отслеживания пополнения конкретного SKU без повторного сканирования каталога.
Комбинирование двух подходов снижает объём запросов — и пропускную способность. Откройте каталог один раз с полной конечной точкой, сохраните интересующие вас идентификаторы, затем регулярно опрашивайте отдельные продукты или коллекции. Этот шаблон — разница между монитором, который масштабируется до сотен магазинов, и тем, который тихо сжигает ваш бюджет на прокси, повторно загружая каталоги, которые не изменились.
Превратите это в мониторинг конкурентов
Настоящая ценность — не одноразовый дамп каталога, а разница со временем. Делайте снимки products.json конкурента по расписанию, ключ по id варианта, и сравнивайте запуски, чтобы поймать изменения цен, новые продукты и пополнения в момент их появления. Поскольку available и price находятся на каждом варианте, вы получаете сигналы о наличии и ценах, не касаясь страницы продукта.
def snapshot(base):
rows = {}
for p in all_products(base):
for v in p["variants"]:
rows[v["id"]] = {
"product": p["title"],
"sku": v["sku"],
"price": v["price"],
"available": v["available"],
}
return rows
# diff today's snapshot against yesterday's to flag price + stock moves
def changes(old, new):
for vid, cur in new.items():
prev = old.get(vid)
if prev and (prev["price"] != cur["price"] or prev["available"] != cur["available"]):
yield cur["sku"], prev, cur
Это основа мониторинга цен конкурентов и исследования дропшиппинга — оба опираются на один и тот же шаблон снимков и различий по многим магазинам одновременно.
Получите быстрые датацентровые прокси для скрапинга Shopify

Какие прокси вам действительно нужны
Конечная точка products.json — это публичный JSON, а не защищённый процесс оформления заказа, поэтому она прощает — но скрапьте сотни магазинов или атакуйте один по расписанию, и вы столкнётесь с ограничениями по скорости для каждого IP. Экономичный ответ — быстрые датацентровые прокси с ротацией: дешёвые, быстрые и достаточно, чтобы распределить запросы по IP, чтобы ни один из них не был ограничен. Зарезервируйте резидентные IP для магазинов, которые блокируют диапазоны датацентров. Если магазин отключил products.json и скрывает свой каталог за рендерингом страниц или защитой от ботов, Scraper API, который рендерит и вращает за вас, будет более чистым вариантом. Наш гайд по тому, когда выигрывают датацентровые прокси охватывает это решение.
Часто задаваемые вопросы
Как получить все продукты из магазина Shopify в формате JSON?
Запросите /products.json на домене магазина с ?limit=250&page=N и увеличивайте page, пока страница не вернёт пустой массив продуктов. Каждый ответ содержит до 250 продуктов с их вариантами, ценами, SKU и флагами наличия. Для больших каталогов используйте бинарный поиск последней страницы и загружайте страницы одновременно.
Какое ограничение на Shopify products.json?
Публичная конечная точка ограничена 250 продуктами на страницу — это максимальное значение, которое принимает limit. Передача большего числа не вернёт больше; вам всё равно придётся проходить через каталог. Это жёсткое ограничение Shopify, поэтому планируйте пагинацию, а не пытайтесь получить всё в одном запросе.
Является ли скрапинг Shopify products.json законным?
Конечная точка предоставляет общедоступные данные о продуктах без входа в систему, и суды США в целом поддерживали скрапинг публичных данных. Тем не менее, уважайте условия использования магазина, избегайте личных данных и не перегружайте сервер. Это общая информация, а не юридическая консультация — проверьте специфику для вашего случая использования и юрисдикции.
Почему products.json возвращает 404 или пустой ответ?
Либо магазин не на Shopify, либо продавец отключил конечную точку на пользовательской сборке. Некоторые магазины также ограничивают повторяющиеся запросы с одного IP, что может выглядеть как ошибка. Подтвердите, что магазин на Shopify, смените IP и добавьте задержку между запросами, прежде чем предполагать, что конечная точка исчезла.
Конечная точка products.json превращает скрапинг Shopify из задачи по управлению селекторами в чистую загрузку JSON: полный каталог, цены и наличие в одном URL, 250 на страницу, быстро для сканирования с бинарным поиском и тривиально для различия в мониторинге конкурентов. Откройте один раз, опрашивайте коллекции и продукты, которые имеют значение, распределяйте запросы по вращающимся датацентровым IP, и вы сможете отслеживать сотни магазинов по расписанию без единого заблокированного запуска — без парсера HTML, без безголового браузера, без хрупких селекторов, которые нужно нянчить.