Cómo scrapear tiendas Shopify con proxies: guía completa

Aprenda cómo raspar Shopify store data using JSON API endpoints and residential proxies. Código completo Python y Node.js para extraer productos, precios y datos de inventario.

Cómo scrapear tiendas Shopify con proxies: guía completa

¿Por qué Scrape Shopify Stores?

Shopify powers over 4 million online stores worldwide, from small independent brand to major retailers. Esto lo convierte en una de las fuentes más ricas de la inteligencia del comercio electrónico. Al raspar tiendas Shopify, puede rastrear los precios de la competencia, monitorear los lanzamientos de productos, analizar las tendencias del mercado y construir bases de datos de productos integrales.

La buena noticia es que Shopify tiene una estructura predecible que hace que la chatarra sea más sistemática que la mayoría de las plataformas de comercio electrónico. Cada tienda Shopify expone ciertos datos a través de puntos finales estandarizados, lo que significa que una arquitectura de raspadores puede funcionar a través de miles de tiendas diferentes. Para una visión general más amplia de las estrategias de desguace de comercio electrónico, vea nuestra Guía de intercambio electrónico de datos.

Comprender la estructura de la tienda

Cada tienda Shopify sigue los mismos patrones de URL y datos, independientemente del tema o la personalización.

Public JSON Endpoints

Shopify expone los datos de producto a través de JSON endpoints que no requieren autenticación. Esta es la forma más eficiente de chatear Shopify tiendas porque obtienes datos estructurados sin pares HTML.

Public JSON Endpoints
Punto finalDatos devueltosPagination
/products.jsonTodos los productos con variantes, precios, imágenes?page=N&limit=250
/products/{handle}.jsonIndividual detalle de productoN/A
/collections.jsonTodas las colecciones?page=N
/collections/{handle}/products.jsonProductos en una colección?page=N&limit=250
/meta.jsonAlmacene metadatos (nombre, descripción)N/A

Estructura de datos del producto

Cada objeto de producto de la API de JSON incluye:

  • Información básica: título, mango (slug), body html (descripción), proveedor, product type, etiquetas
  • Variantes: Cada variante tiene su propio precio, comparar at price, SKU, estado de inventario y valores de opción (tamaño, color, etc.)
  • Imágenes: URL para todas las imágenes de producto con texto alt
  • Fechas: created at, updated at, published at

Tasa de limitación

Shopify aplica límites de tarifas para proteger el rendimiento de la tienda. Los endpoints públicos de JSON suelen permitir 2-4 solicitudes por segundo por IP antes de empezar a tropezar. Aquí es donde proxies residenciales volverse esencial — propagar las solicitudes a través de múltiples IPs le permite mantener el rendimiento sin limitar la tasa de golpes en cualquier IP individual.

Configuración Proxy para Shopify

El límite de velocidad de Shopify es basado en IP, haciendo que la rotación proxy la estrategia primaria para raspar a escala.

Configuración ProxyHat

# Rotating residential proxy (new IP per request)
http://USERNAME:PASSWORD@gate.proxyhat.com:8080
# Geo-targeted for region-specific stores
http://USERNAME-country-US:PASSWORD@gate.proxyhat.com:8080
# Sticky session for paginated scraping of one store
http://USERNAME-session-shopify001:PASSWORD@gate.proxyhat.com:8080

Para Shopify scraping, use per-request rotación al raspar diferentes tiendas, y sesiones pegajosas al paginar a través del catálogo de productos de una sola tienda. Este patrón imita el comportamiento de navegación natural.

Python Implementation

Aquí hay un raspador Shopify listo para la producción El SDK Python de ProxyHat.

JSON API Scraper

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
# Example: Scrape multiple Shopify stores
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")
        # Save to JSON
        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))

Cambios de precios de monitoreo en todas las tiendas

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 Implementation

Versión Node.js usando Nodo de ProxyHat SDK.

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++;
      // Random delay 1-3 seconds
      await new Promise((r) => setTimeout(r, 1000 + Math.random() * 2000));
    } catch (err) {
      console.error(`Error on page ${page}: ${err.message}`);
      break;
    }
  }
  return products;
}
async function scrapeMultipleStores(stores) {
  const results = {};
  for (const store of stores) {
    console.log(`\nScraping: ${store}`);
    const products = await scrapeShopifyProducts(store);
    results[store] = products;
    console.log(`Found ${products.length} products`);
    // Delay between stores
    await new Promise((r) => setTimeout(r, 3000 + Math.random() * 4000));
  }
  return results;
}
// Usage
scrapeMultipleStores([
  "example-store-1.myshopify.com",
  "example-store-2.com",
]).then((results) => {
  fs.writeFileSync("shopify_data.json", JSON.stringify(results, null, 2));
  console.log("Data saved to shopify_data.json");
});

Shopify-Specific Scraping Strategies

Descubriendo Tiendas

Antes de raspar, usted necesita identificar qué sitios de la competencia funcionan en Shopify. Los indicadores comunes incluyen:

  • El /products.json endpoint devuelve valid JSON
  • fuente HTML contiene Shopify.theme o cdn.shopify.com
  • El x-shopify-stage header is present in responses

Manejo de tiendas con contraseña

Algunas tiendas Shopify requieren una contraseña para acceder. Estas son típicamente tiendas pre-lanzamiento o mayorista. Los endpoints JSON devolverán una redireccion a la página de contraseña. Saltar estas tiendas en su tubería de desguace a menos que haya autorizado el acceso.

Hacer frente a dominios personalizados

Tiendaify tiendas a menudo utilizan dominios personalizados en lugar de .myshopify.com. La API de JSON funciona de la misma manera en los dominios personalizados. Simplemente use el dominio de la tienda en sus solicitudes.

Seguimiento de inventarios

Las variantes de productos incluyen un available campo que indica el estado de stock. Mediante el seguimiento de este campo con el tiempo, puede monitorear los niveles de inventario de los competidores e identificar cuando los productos salen de stock — inteligencia útil para fijar precios y restaurar decisiones.

Evitar bloques y límites de tarifas

Mientras Shopify es más fácil de raspar que Amazon, todavía impone protecciones.

Evitar bloques y límites de tarifas
ProtecciónDetallesMitigation
Limitación de la tasa de IP~2-4 req/sec por IP for JSON endpointsRotate proxies residenciales a través de solicitudes
Protección de la nubeAlgunas tiendas usan CloudflareIPs residenciales con encabezados tipo navegador
Detección de botasPatrones conductuales supervisadosRetrasos aleatorios y agentes de usuario
Páginas de contraseñaTiendas pre-lanzamiento/al por mayor cerradasSaltar o utilizar el acceso autorizado

Para más información sobre el manejo de sistemas antibots, lea nuestra guía cómo raspar sitios web sin ser bloqueado.

Key takeaway: Shopify's JSON API es el método de desguace más eficiente — le da datos estructurados sin pares HTML. Úsalo antes de volver a la chatarra HTML.

Casos de uso de datos

Una vez que haya recogido los datos del producto Shopify, aquí están las aplicaciones más valiosas:

  • Precio competitivo: Rastree los precios de los competidores en las categorías de productos y ajuste su estrategia de precios en tiempo real.
  • Investigación de productos: Identificar productos de tendencia, nuevos lanzamientos y brechas de mercado mediante la vigilancia de múltiples tiendas.
  • Análisis de mercado: Aggregate data across hundreds of Shopify stores to understand market trends, pricing distribution, and category growth.
  • Enriquecimiento del catálogo: Utilice descripciones de productos competidores, imágenes y especificaciones para mejorar sus propios anuncios.
  • Supervisión de la marca: Pista a vendedores no autorizados de sus productos y monitoree el cumplimiento de MAP en Shopify storefronts.

Key Takeaways

  • Shopify's /products.json endpoint es el método de raspado más eficiente — utilizarlo antes del análisis HTML.
  • Una arquitectura de un solo raspador trabaja en todas las tiendas Shopify debido a la estructura estandarizada.
  • Los proxies residenciales con la rotación superan la tasa IP de Shopify.
  • Usar sesiones pegajosas al paginar a través del catálogo de una sola tienda.
  • Seguimiento de precios y disponibilidad de nivel variable para una inteligencia competitiva global.
  • Empieza con Los proxies residenciales de ProxyHat para escalar su Shopify chatarra fiable.

¿Listo para empezar a raspar tiendas Shopify? Explore nuestra Guía de intercambio electrónico de datos para la estrategia completa, y comprobar nuestra Python guía proxy y Node.js guía proxy para detalles de la aplicación. Visita nuestra página de precios para empezar.

¿Listo para empezar?

Accede a más de 50M de IPs residenciales en más de 148 países con filtrado impulsado por IA.

Ver preciosProxies residenciales
← Volver al Blog