プロキシでShopifyストアをスクレイピングする方法:完全ガイド

JSON API エンドポイントと住宅プロキシを使用して、Shopify ストアデータをスクレイピングする方法を学びます。 製品、価格、在庫データを抽出するためのPythonおよびNode.jsコードを完了します.

プロキシでShopifyストアをスクレイピングする方法:完全ガイド

ShopifyストアをScrapeする理由

小規模な独立したブランドから大手小売店まで、世界中で4万以上のオンラインストアを販売しています。 これは、eコマースインテリジェンスの最も豊富なソースの1つです。 Shopifyストアをスクレイピングすることで、競合他社の価格設定を追跡し、製品の発売を監視し、市場動向を分析し、包括的な製品データベースを構築することができます。

グッドニュースは、Shopifyは、ほとんどの電子商取引プラットフォームよりも、より体系的にスクレイピングする予測可能な構造を持っていることです。 Shopifyストアは、標準化されたエンドポイントを介して特定のデータを露出します。つまり、単一のスクレーパーアーキテクチャは、数千の異なる店舗で作業することができます。 eコマーススクレイピング戦略の広範な概要については、当社のを参照してください。 eコマースデータスクレイピングガイド. .

Shopifyのストア構造を理解する

Shopifyストアは、テーマやカスタマイズに関係なく、同じURLとデータパターンをフォローします。

パブリックJSONエンドポイント

Shopifyは認証を必要としないJSONエンドポイントで製品データを露出します。 これらは、HTMLパーシングなしで構造化されたデータを取得しているため、Shopifyストアをスクレイピングするための最も効率的な方法です。

パブリックJSONエンドポイント
エンドポイント返されるデータパジネーション
/products.json品種、価格、画像のすべての製品?page=N&limit=250
/products/{handle}.json単一プロダクト細部N・A
/collections.jsonすべてのコレクション?page=N
/collections/{handle}/products.jsonコレクション内の製品?page=N&limit=250
/meta.jsonメタデータを保存(名前、説明)N・A

製品データ構造

JSON API の各製品オブジェクトには、以下が含まれます。

  • 基本情報: title, handle (slug), body html (description), ベンダー, product type, タグ
  • バリアント: 各 variant には独自の価格、Compared at price、SKU、在庫状況、オプション値(サイズ、色など)があります。
  • イメージ: altテキストですべての製品イメージのURL
  • 日付: create at, updated at, 公開 at

レート制限

Shopifyは、店舗のパフォーマンスを保護するために、レート制限が適用されます。 一般的なJSONエンドポイントは通常、スロットリングキックの前に、IPごとに2-4リクエストを割り当てます。 これは、 住宅のプロキシ 複数の IP 間でリクエストを広めることで、単一の IP でレート制限を打つことなくスループットを維持できます。

Shopifyのプロキシ構成

Shopifyのレート制限はIPベースで、プロキシの回転をスケールでスクレイピングするための主な戦略です。

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

Shopifyのスクレイピングのために、異なる店舗をスクレイピングするとき、および1つのストアの製品カタログをパギンするときに粘着セッションをスクレイピング使用してください。 このパターンは自然な閲覧行動を模倣します。

Pythonの実装

ここでは、生産準備のShopifyスクレーパーを使用して ProxyHatのPython SDK. .

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
# 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))

店舗間での価格変更の監視

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 バージョン 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スクレイピング戦略

Shopifyストアを発見

スクレイピングする前に、どの競合サイトがShopifyで実行されているかを識別する必要があります。 共通の表示器は下記のものを含んでいます:

  • ザ・オブ・ザ・ /products.json endpoint は有効な JSON を返します
  • HTML ソースが含まれています Shopify.theme または cdn.shopify.com
  • ザ・オブ・ザ・ x-shopify-stage ヘッダはレスポンスに存在する

パスワードストアの取り扱い

Shopifyストアにはパスワードが必要です。 これらは通常、プレランチまたは卸売店です。 JSON エンドポイントは、パスワードページのリダイレクトを返します。 許可されたアクセスがない限り、これらのストアをスクレイピングパイプラインにスキップします。

カスタムドメインを扱う

Shopifyストアは、代わりにカスタムドメインを使用することが多い .myshopify.com. JSON API はカスタムドメインと同じ方法で動作します。 リクエストでストアのパブリックフェーシングドメインを使用するだけです。

在庫追跡

製品の種類には、 available 在庫状況を示すフィールド。 時間の経過とともにこのフィールドを追跡することで、競合他社の在庫レベルを監視し、製品が在庫切れるときに識別することができます。価格設定と再入荷決定のための有用な知能。

ブロックやレート制限を回避

ShopifyはAmazonよりもスクレーパーフレンドリーですが、保護を強化しています。

ブロックやレート制限を回避
ソリューションニュースマイティグレーション
IP率制限~2-4 JSON エンドポイントの IP ごとの req/secリクエスト間で住宅のプロキシを回転
クラウドフレア保護Cloudflareを使用している店舗ブラウザのようなヘッダーを持つ住宅IP
ボット検出行動パターンを監視遅延とユーザーエージェントをランダム化
パスワードページプレランチ/卸売店ロック許可されたアクセスをスキップまたは使用

アンチボットシステムの取り扱いについて詳しくは、ガイドをご覧ください ブロックせずにウェブサイトをスクレイピングする方法. .

キーテイクアウト: ShopifyのJSON APIは、最も効率的なスクレイピングアプローチです。これにより、HTMLパーシングなしで構造化されたデータが得られます。 HTMLのスクレイピングに戻る前に使用してください。

データユースケース

Shopify製品データを収集したら、最も価値のあるアプリケーションは次のとおりです。

  • 競争価格: 競合他社の価格を追跡し、価格設定戦略をリアルタイムで調整します。
  • プロダクト研究: 複数の店舗を監視することで、トレンド商品、新規発売、市場ギャップを特定します。
  • 市場分析: マーケットトレンド、価格設定分布、カテゴリの成長を理解するために、Shopifyストアの何百ものデータを集計します。
  • カタログの濃縮: 競合製品の説明、画像、仕様を使用して、独自のリストを改善します。
  • ブランド監視: 製品の無許可の売り手を追跡し、ShopifyストアフロントにMAPコンプライアンスを監視します。

キーテイクアウト

  • Shopifyの /products.json エンドポイントは最も効率的なスクレイピング方法です。HTML 解析の前に使用します。
  • シングルスクレーパーアーキテクチャは、標準化された構造により、すべてのShopifyストアで動作します。
  • ShopifyのIPベースのレート制限を克服した住宅のプロキシ。
  • シングルストアのカタログを通した際の粘液セッションを使用してください。
  • 包括的な競争力のあるインテリジェンスのために、バリアントレベルの価格設定と可用性を追跡します。
  • まずは ProxyHatの住宅用プロキシ 確実にスクレイピングをスケールアップ

Shopifyストアをスクラップする準備はできましたか? 詳しくはこちら eコマースデータスクレイピングガイド 完全な戦略のために、そして私達の点検して下さい Pythonプロキシガイド そして、 Node.jsプロキシガイド 実装の詳細 訪問する プライシングページ はじめに。

始める準備はできましたか?

AIフィルタリングで148か国以上、5,000万以上のレジデンシャルIPにアクセス。

料金を見るレジデンシャルプロキシ
← ブログに戻る