PythonでShopifyをスクレイピングして市場調査を自動化する方法

最終更新日 April 16, 2026

Shopify の /products.json エンドポイントは、EC データの世界では“ほぼみんな知ってる裏ワザ”みたいな存在です。ストアの URL の末尾にこれを付けるだけで、API キーも認証もいらず、HTML を面倒くさくたどらなくても、構造化された JSON をそのまま取れます。

私は のチームで働いていて、毎日のように「どうやって Web からデータを抜き出すか」を考えています。なかでも Shopify のスクレイピングは相談が本当に多いです。競合価格を追いたい営業チーム、商品カタログを比べたい EC 運用担当、仕入れ先を探したい購買担当など、用途はかなり幅広いです。Shopify には があり、米国 EC 市場の約 を占めているので、取れる商品データの量もかなり大きいです。

このガイドでは、エンドポイントが何を返すのか、数千件の商品をページネーションでどう集めるのか、ブロックされずにレート制限へどう対応するのか、そして pandas を使って Shopify のネストされた JSON を見やすい CSV や Excel に整える方法まで、ひと通りまとめて解説します。さらに、あまり他では触れられないエンドポイント(/collections.json/meta.json)や、Python を使わずに済ませたい人向けのノーコード代替案も紹介します。

Shopify の /products.json エンドポイントとは? なぜスクレイピングしやすいのか

Shopify ストアには、公開エンドポイント {store-url}/products.json があり、そこから商品データを構造化された形で取得できます。API キーは不要。OAuth も不要。認証も一切ありません。ストア URL に /products.json を付けるだけで、カタログ内の全商品が JSON 配列として返ってきます。

実際に試してみましょう。 をブラウザで開いてみてください。商品名、価格、バリエーション、画像、タグなど、きれいに整理された JSON が表示されるはずです。

これを HTML を解析する方法と比べると、かなり差があります。Shopify の HTML テーマは入れ子が深く、ストアごとの差も大きく、しかも加盟店がテーマを更新するたびに構造が変わります。たとえば、こんな違いです。

HTML 方式(大変):

1<div class="product-card__info">
2  <h3 class="product-card__title">
3    <a href="/products/classic-blue-jeans">Classic Blue Jeans</a>
4  </h3>
5  <span class="price price--on-sale" data-product-price>$149.00</span>
6</div>

JSON 方式(シンプル):

1{
2  "title": "Classic Blue Jeans",
3  "handle": "classic-blue-jeans",
4  "vendor": "Hiut Denim",
5  "variants": [{"price": "149.00", "sku": "HD-BLU-32", "available": true}]
6}

一貫性、信頼性、解析しやすさの面では、JSON のほうが圧倒的に有利です。さらにこのエンドポイントは、?limit=(1ページ最大250件、デフォルト30件)と、ページ送りに使う ?page= という2つの重要なクエリパラメータにも対応しています。後ほどのコードでしっかり使っていきます。

ここで大事なのは、これは 公開ストアフロントのエンドポイント であって、 ではないという点です。Admin API にはストア所有者のアクセストークンが必要で、注文データ、在庫、顧客情報などにアクセスできます。一方、公開 /products.json は誰でもアクセスできる読み取り専用の商品データです。この違いは後ほど詳しく説明します。というのも、ここを混同する人がかなり多いからです。

注意点: すべての Shopify ストアがこのエンドポイントを公開しているわけではありません。私のテストでは、約71%のストアで有効な JSON が返りました(allbirds.com、gymshark.com、colourpop.com、kyliecosmetics.com などは動作確認済み)一方で、カスタム設定の一部では 404 になることもあります(hiutdenim.co.uk、bombas.com など)。確認方法はシンプルで、{store-url}/products.json をブラウザで開いて結果を見るだけです。

なぜ Python で Shopify をスクレイピングするのか? 代表的なビジネス用途

では、なぜわざわざやるのか。答えは ROI です。 が、今では競合分析のために自動価格収集を使っています。2020年の34%から大きく伸びました。さらに、 という研究もあります。つまり、データは本当にお金になるということです。

よくある用途は次のとおりです。

用途恩恵を受ける人得られる情報
競合価格の監視EC 運用チーム競合カタログ全体の価格変動、割引、比較価格を追跡
商品調査・仕入れ購買 / MD 部門商品特性、バリエーション、素材、在庫状況を比較
リード獲得営業チームストアカタログから仕入れ先名、ブランド情報、連絡先を抽出
市場・カテゴリ分析マーケティングチーム商品構成、タグ、コレクション構造、ポジショニングを把握
在庫・供給状況の追跡サプライチェーンチームバリエーションごとの在庫状態(available: true/false)を継続監視
新商品検知プロダクトチームcreated_at を追って競合の新規発売を検知

この用途には Python がかなり相性いいです。 が Python を主言語として使っており、requests による HTTP 通信、pandas によるデータ整形、httpx による非同期処理など、エコシステムが充実しているので、URL を持った状態からスプレッドシートを作るところまでを80行未満のコードで実現できます。

products.json の完全フィールド一覧:各項目の意味を整理

他の解説記事は titleidhandle だけ紹介して終わりがちですが、Shopify の JSON レスポンスには、商品・バリエーション・画像・オプションをまたいで40項目以上のフィールドがあります。スクレイピングを書く前に何が取れるのか把握しておくと、後から取り直す手間を減らせます。

このリファレンスは、2026年4月16日に取得した実際の /products.json レスポンスをもとに整理しています。構造は、このエンドポイントを公開しているすべてのストアで共通です。

商品レベルのフィールド

フィールドデータ型活用例
idInteger123456789重複排除のための一意な商品ID
titleString"Classic Blue Jeans"カタログや比較用の商品名
handleString"classic-blue-jeans"URL スラッグ。商品ページは {store}/products/{handle} で生成可能
body_htmlString (HTML) or null

Our best-selling...

コンテンツ分析や SEO 調査向けの商品説明
vendorString"Hiut Denim"リード獲得や仕入れ先調査に使うブランド / 供給元名
product_typeString"Jeans"市場分析のためのカテゴリ分類
created_atISO DateTime"2024-01-15T10:30:00-05:00"商品追加時期の把握(新商品検知)
updated_atISO DateTime"2025-03-01T08:00:00-05:00"直近のカタログ変更を検知
published_atISO DateTime"2024-01-16T00:00:00-05:00"ストアフロントで公開された日時を把握
tagsArray of Strings["organic", "women", "straight-leg"]SEO、分類、トレンド把握のためのタグ分析
variantsArray of Objects(see variant fields below)バリエーションごとの価格、SKU、在庫状況
imagesArray of Objects(see image fields below)カタログや視覚分析向けの商品画像 URL
optionsArray of Objects[{"name": "Size", "values": ["S","M","L"]}]サイズ、色、素材などの構成を把握

バリエーションレベルのフィールド(各商品内にネスト)

フィールドデータ型用途
idInteger987654321バリエーションの一意識別子
titleString"32 / Blue"バリエーションの表示名
skuString"HD-BLU-32"在庫管理システムとの SKU 照合
priceString"185.00"価格監視(文字列なので計算時は float に変換)
compare_at_priceString or null"200.00"元値。割引監視に必須
availableBooleantrue在庫有無の判定(公開情報で使える唯一の在庫指標)
weightFloat1.2配送・物流分析
option1, option2, option3String"32", "Blue", null各オプション値
created_at, updated_atISO DateTimeバリエーション単位の変更追跡

画像レベルのフィールド

フィールドデータ型用途
idInteger111222333画像の一意識別子
srcString (URL)"https://cdn.shopify.com/..."画像の直接ダウンロードリンク
altString or null"Front view of jeans"アクセシビリティ分析用の alt テキスト
positionInteger1画像の並び順
width, heightInteger2048, 2048画像サイズ

公開エンドポイントに含まれないもの

見落としやすい大事なポイントがあります。inventory_quantity は公開 /products.json レスポンスには含まれません。 この項目はセキュリティ上の理由から、2017年12月に公開 JSON エンドポイントから外されました。取得できる在庫指標は、各バリエーションの availabletrue または false)だけです。実在庫数を取るには、ストア所有者の認証情報を使った Admin API が必要です。

スクレイピングのコードを書く前にこの表を見て、どの項目が必要かを決めてください。価格監視なら variants[].pricevariants[].compare_at_pricevariants[].available が必要です。リード獲得なら vendorproduct_typetags に注目します。用途に合わせて絞り込めば、CSV はかなり見やすくなります。

products.json だけじゃない:Collections、Meta などの Shopify エンドポイント

競合記事ではあまり触れられない内容ですが、本格的な競合分析ではかなり重要です。

/collections.json — ストア内の全コレクション

タイトル、handle、説明、商品数を含む、ストア内のすべてのコレクション(カテゴリ)を返します。zoologistperfumes.com、allbirds.com、gymshark.com で確認済みで、いずれも有効な JSON が返りました。

1{
2  "collections": [
3    {
4      "id": 308387348539,
5      "title": "Attars",
6      "handle": "attars",
7      "published_at": "2026-03-29T12:20:32-04:00",
8      "products_count": 1,
9      "image": { "src": "https://cdn.shopify.com/..." }
10    }
11  ]
12}

競合がどうやって商品構成を整理しているかを知りたいなら、このエンドポイントが役立ちます。

/collections/{handle}/products.json — カテゴリ別の商品

特定コレクションで絞り込んだ商品を返します。構造は /products.json と同じですが、1カテゴリに限定されます。たとえば競合の「Sale」や「New Arrivals」だけを追いたい場合にかなり便利です。

/meta.json — ストア全体のメタデータ

ストア名、説明、通貨、国、そして便利なのが published_products_count を返します。この件数があれば、必要なページ数を ceil(published_products_count / 250) で事前に計算できます。空レスポンスが出るまで延々とページを回す必要はありません。

どのエンドポイントを使うべきか?

取得したいものエンドポイント認証は必要?
全商品(公開)/products.jsonいいえ
特定カテゴリの商品/collections/{handle}/products.jsonいいえ
ストアメタデータ + 商品数/meta.jsonいいえ
全コレクション(カテゴリ)/collections.jsonいいえ
注文 / 売上データ(自社ストアのみ)Admin API /orders.jsonはい(APIキー)
在庫数量(自社ストアのみ)Admin API /inventory_levels.jsonはい

よくある質問「競合が何個売ったかは取れますか?」への短い答えは、いいえです。公開エンドポイントからは無理です。売上データや在庫数量は認証済みの Admin API が必要で、つまりストア所有者としてのアクセス権が必要になります。公開エンドポイントで取れるのは商品カタログ情報だけです。

shopify-data-access-methods.webp

Python で Shopify をスクレイピングする手順:セットアップから実行まで

  • 難易度: 初級
  • 所要時間: 約15分(セットアップ + 初回実行)
  • 必要なもの: Python 3.11 以上、pip、ターミナル、そして対象の Shopify ストア URL

ステップ1:Python と必要なライブラリをインストールする

Python 3.11 以上が入っていることを確認してください(pandas 3.0.x では必須です)。そのうえで、必要なライブラリを2つインストールします。

1pip install requests pandas

Excel 出力もしたいなら、こちらも入れておきましょう。

1pip install openpyxl

スクリプトの先頭には、以下を追加します。

1import requests
2import pandas as pd
3import time
4import random
5import json

実行時に import エラーが出なければOKです。pandas がバージョンエラーを出す場合は、Python を 3.12 に更新してください。

ステップ2:/products.json から商品データを取得する

以下は、ストア URL を受け取り、エンドポイントへアクセスして、パース済み JSON を返す基本関数です。

1def fetch_products_page(store_url, page=1, limit=250):
2    """Fetch a single page of products from a Shopify store."""
3    url = f"{store_url.rstrip('/')}/products.json"
4    params = {"limit": limit, "page": page}
5    headers = {
6        "User-Agent": "Mozilla/5.0 (compatible; ProductResearch/1.0)"
7    }
8    response = requests.get(url, params=params, headers=headers, timeout=30)
9    response.raise_for_status()
10    return response.json().get("products", [])

ポイントは以下のとおりです。

  • limit=250 は Shopify が1ページあたりに許可する最大値です。デフォルトは30件なので、明示的に指定するとリクエスト回数を最大8分の1まで減らせます。
  • User-Agent ヘッダー は、できるだけ自然なものを設定しましょう。User-Agent なしのリクエストは、Shopify のボット対策に引っかかりやすくなります。
  • timeout=30 は、1回のリクエストがいつまでも止まらないようにするためです。

既知のストアで試してみましょう。

1products = fetch_products_page("https://allbirds.com")
2print(f"Fetched {len(products)} products")
3print(f"First product: {products[0]['title']}")

Fetched 250 products と最初の商品名が出れば成功です。

ステップ3:ページネーション対応で全商品を取得する

1回のリクエストで取得できるのは最大250件です。実際にはそれ以上の商品を持つストアも多くあります(Allbirds は 1,420 件以上)。空のレスポンスが返るまでページを回す必要があります。

1def scrape_all_products(store_url, delay=1.0):
2    """Scrape all products from a Shopify store, handling pagination."""
3    all_products = []
4    page = 1
5    while True:
6        print(f"Fetching page {page}...")
7        products = fetch_products_page(store_url, page=page, limit=250)
8        if not products:
9            print(f"No more products. Total: {len(all_products)}")
10            break
11        all_products.extend(products)
12        print(f"  Got {len(products)} products (total so far: {len(all_products)})")
13        page += 1
14        # 礼儀として、リクエスト間に待機を入れる
15        time.sleep(delay + random.uniform(0, 0.5))
16    return all_products

products が空になったら終端に到達したということです。

time.sleep() にランダムな揺らぎを入れることで、Shopify の事実上のレート制限(約2リクエスト/秒)を超えにくくなります。

ワンポイント: 先に /meta.json を取っておけば、総商品数が分かるので、必要ページ数を pages = ceil(product_count / 250) で事前計算できます。これにより、最後に「空の1回」を余計に叩かなくて済みます。

ステップ4:必要なフィールドだけを取り出す

すべての商品を Python の辞書リストとして取得できたら、必要な項目だけを抜き出します。ここでは、価格監視でよく使う項目を抽出する例を示します。

1def extract_product_data(products):
2    """Extract key fields from products, flattening variants."""
3    rows = []
4    for product in products:
5        for variant in product.get("variants", []):
6            rows.append({
7                "product_id": product["id"],
8                "title": product["title"],
9                "handle": product["handle"],
10                "vendor": product.get("vendor", ""),
11                "product_type": product.get("product_type", ""),
12                "tags": ", ".join(product.get("tags", [])),
13                "created_at": product.get("created_at", ""),
14                "variant_id": variant["id"],
15                "variant_title": variant.get("title", ""),
16                "sku": variant.get("sku", ""),
17                "price": variant.get("price", ""),
18                "compare_at_price": variant.get("compare_at_price", ""),
19                "available": variant.get("available", ""),
20                "image_url": product["images"][0]["src"] if product.get("images") else ""
21            })
22    return rows

これはバリエーションごとに1行を作る形です。たとえば「Classic Blue Jeans」のような商品には、6サイズ × 2色で12バリエーションあるかもしれず、それぞれ価格や在庫状態が異なります。価格比較にはこの形式がかなり向いています。

pandas で Shopify データを CSV と Excel に出力する

他の Shopify スクレイピング記事では、JSON をそのままファイルに保存して終わり、というケースが多いです。開発者にはそれで十分でも、金曜までにスプレッドシートが欲しい EC アナリストには使いにくいです。

問題は、Shopify の JSON がネスト構造になっていることです。1つの商品に複数のバリエーションがあり、それぞれに価格、SKU、在庫が付いています。これを行と列に平坦化するには、pandas の処理が必要です。

ネストした JSON を見やすい表に整える

用途によって、2つのやり方があります。

方法A:1バリエーションにつき1行(価格監視や在庫追跡に最適)

1# ステップ4の extract_product_data 関数を使用
2products = scrape_all_products("https://allbirds.com")
3rows = extract_product_data(products)
4df = pd.DataFrame(rows)
5print(f"DataFrame shape: {df.shape}")
6print(df.head())

これで、各行がユニークな商品×バリエーションの組み合わせになります。500商品で平均4バリエーションなら、約2,000行の DataFrame になります。

方法B:1商品につき1行の要約(カタログ全体の俯瞰に最適)

1def summarize_products(products):
2    """One row per product with min/max price across variants."""
3    rows = []
4    for product in products:
5        prices = [float(v["price"]) for v in product.get("variants", []) if v.get("price")]
6        rows.append({
7            "product_id": product["id"],
8            "title": product["title"],
9            "vendor": product.get("vendor", ""),
10            "product_type": product.get("product_type", ""),
11            "variant_count": len(product.get("variants", [])),
12            "min_price": min(prices) if prices else None,
13            "max_price": max(prices) if prices else None,
14            "any_available": any(v.get("available", False) for v in product.get("variants", [])),
15            "tags": ", ".join(product.get("tags", []))
16        })
17    return rows

CSV、Excel、Google Sheets に出力する

1# CSV 出力(Excel で文字化けしにくいよう utf-8-sig を使用)
2df.to_csv("shopify_products.csv", index=False, encoding="utf-8-sig")
3# Excel 出力(openpyxl が必要)
4df.to_excel("shopify_products.xlsx", index=False, engine="openpyxl")
5print("shopify_products.csv と shopify_products.xlsx に出力しました")

Google Sheets に送るなら gspread とサービスアカウントを使えますが、正直、多くの用途では CSV に出して Google Drive に上げるほうが速くて簡単です。

本番向けの Python スクレイピング:レート制限、リトライ、ブロック回避

基本スクリプトは小規模ストアなら問題なく動きます。でも、5,000件超の商品を持つストアや、複数ストアを連続で扱うときは話が変わります。そういう場面では、つまずきやすくなります。

Shopify のレート制限とブロックの挙動を理解する

Shopify の公開 JSON エンドポイントには、Admin API のような正式なレート制限仕様はありませんが、実測では次の傾向があります。

  • 安全圏: ストアごとに約2リクエスト/秒
  • ソフト上限: 約40リクエスト/分を超えると制限がかかりやすい
  • HTTP 429: "Too Many Requests"。一般的なレート制限レスポンス
  • HTTP 430: Shopify 独自のコードで、単なるレート制限ではなくセキュリティブロックを示す場合がある
  • HTTP 403 または CAPTCHA リダイレクト: 追加の Cloudflare 保護があるストアで発生することがある

AWS Lambda や Google Cloud Run のような共有クラウド基盤からのアクセスは、IP レンジの悪用履歴が多いため、特にブロックされやすいです。

安定して Shopify を取得するための段階的な対策

「自分のPCでは動く」から「本番運用できる」までの違いは以下です。

レベル手法信頼性
基本requests.get() + ?page=大規模カタログでは失敗しやすく、ブロックされることもある
中級requests.Session() + ?limit=250 + time.sleep(1) + 429 時に再試行ほとんどのストアで動作
上級非同期 httpx + User-Agent ローテーション + 指数バックオフ本番向け、1万件超にも対応可能

中級レベル(多くの人におすすめ):

1import requests
2from requests.adapters import HTTPAdapter
3from urllib3.util.retry import Retry
4def create_session():
5    """Create a requests session with automatic retry logic."""
6    session = requests.Session()
7    retries = Retry(
8        total=5,
9        backoff_factor=1,  # sleep: 0.5s, 1s, 2s, 4s, 8s
10        status_forcelist=[429, 430, 500, 502, 503, 504],
11        respect_retry_after_header=True
12    )
13    session.mount("https://", HTTPAdapter(max_retries=retries))
14    session.headers.update({
15        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
16    })
17    return session

Retry の設定により、429 応答が指数バックオフ付きで自動処理されます。backoff_factor=1 の場合、待機時間は 0.5秒 → 1秒 → 2秒 → 4秒 → 8秒 という流れで増えます。requests.Session() を使って接続を再利用することで、同じドメインへの複数リクエスト時のオーバーヘッドも減らせます。

User-Agent のローテーション: 複数ストアを取得する場合は、現実的なブラウザの User-Agent を3〜5種類ほど切り替えるとよいです。これはごまかしのためというより、毎回同じヘッダーを送る“いかにもボット”な挙動を避けるためです。

CSV 出力まで含めた、完成版の Python スクリプト

以下は、ここまでの内容を全部まとめたコピペ用の完全版スクリプトです。実際のコードは約75行(コメント除く)で、Allbirds(1,420商品)、ColourPop(2,000商品超)、Zoologist Perfumes(小規模カタログ)で動作確認済みです。

1import requests
2import pandas as pd
3import time
4import random
5from requests.adapters import HTTPAdapter
6from urllib3.util.retry import Retry
7def create_session():
8    """Create a session with retry logic for rate limits."""
9    session = requests.Session()
10    retries = Retry(
11        total=5,
12        backoff_factor=1,
13        status_forcelist=[429, 430, 500, 502, 503, 504],
14        respect_retry_after_header=True
15    )
16    session.mount("https://", HTTPAdapter(max_retries=retries))
17    session.headers.update({
18        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
19                      "AppleWebKit/537.36 (KHTML, like Gecko) "
20                      "Chrome/125.0.0.0 Safari/537.36"
21    })
22    return session
23def scrape_shopify(store_url, delay=1.0):
24    """Scrape all products from a Shopify store via /products.json."""
25    session = create_session()
26    all_products = []
27    page = 1
28    base_url = f"{store_url.rstrip('/')}/products.json"
29    while True:
30        print(f"  Page {page}...", end=" ")
31        resp = session.get(base_url, params={"limit": 250, "page": page}, timeout=30)
32        resp.raise_for_status()
33        products = resp.json().get("products", [])
34        if not products:
35            break
36        all_products.extend(products)
37        print(f"{len(products)} products (total: {len(all_products)})")
38        page += 1
39        time.sleep(delay + random.uniform(0, 0.5))
40    return all_products
41def flatten_to_variants(products):
42    """Flatten nested product JSON into one row per variant."""
43    rows = []
44    for p in products:
45        base = {
46            "product_id": p["id"],
47            "title": p["title"],
48            "handle": p["handle"],
49            "vendor": p.get("vendor", ""),
50            "product_type": p.get("product_type", ""),
51            "tags": ", ".join(p.get("tags", [])),
52            "created_at": p.get("created_at", ""),
53            "updated_at": p.get("updated_at", ""),
54            "image_url": p["images"][0]["src"] if p.get("images") else "",
55        }
56        for v in p.get("variants", []):
57            row = {**base}
58            row["variant_id"] = v["id"]
59            row["variant_title"] = v.get("title", "")
60            row["sku"] = v.get("sku", "")
61            row["price"] = v.get("price", "")
62            row["compare_at_price"] = v.get("compare_at_price", "")
63            row["available"] = v.get("available", "")
64            rows.append(row)
65    return rows
66if __name__ == "__main__":
67    STORE_URL = "https://allbirds.com"  # Change this to your target store
68    OUTPUT_CSV = "shopify_products.csv"
69    OUTPUT_EXCEL = "shopify_products.xlsx"
70    print(f"Scraping {STORE_URL}...")
71    products = scrape_shopify(STORE_URL)
72    print(f"\nTotal products scraped: {len(products)}")
73    print("Flattening to variant-level rows...")
74    rows = flatten_to_variants(products)
75    df = pd.DataFrame(rows)
76    print(f"DataFrame: {df.shape[0]} rows x {df.shape[1]} columns")
77    df.to_csv(OUTPUT_CSV, index=False, encoding="utf-8-sig")
78    df.to_excel(OUTPUT_EXCEL, index=False, engine="openpyxl")
79    print(f"\nExported to {OUTPUT_CSV} and {OUTPUT_EXCEL}")

python scrape_shopify.py で実行します。Allbirds なら約45秒で終わり、約5,000件超の行を持つ CSV が生成されます(1バリエーション1行)。ターミナル出力はこんな感じです。

1Scraping https://allbirds.com...
2  Page 1... 250 products (total: 250)
3  Page 2... 250 products (total: 500)
4  ...
5  Page 6... 170 products (total: 1420)
6Total products scraped: 1420
7Flattening to variant-level rows...
8DataFrame: 5680 rows x 14 columns
9Exported to shopify_products.csv and shopify_products.xlsx

Python を使わずに2クリックで Shopify を取得する:Thunderbit のノーコード代替

Python のインストール、import エラーの解消、スクレイピングスクリプトの保守までやりたくない人もいます。明日の朝までに競合価格が必要な営業担当にとっては、Python は少し重すぎるかもしれません。

そこで私たちは を作りました。Chrome 拡張として動く AI ウェブスクレイパーです。コード不要、API キー不要、環境構築も不要です。

Thunderbit は Shopify ストアをどうスクレイピングするのか

Thunderbit には、Shopify の商品ページ向けにあらかじめ設定された専用の Shopify Scraper テンプレート があります。 をインストールして Shopify ストアを開き、「Scrape」をクリックするだけです。テンプレートが商品名、説明、価格、バリエーション詳細、画像、販売元情報を自動で抽出します。

テンプレートがストアにぴったり合わない場合(カスタムテーマや特殊なレイアウトなど)は、Thunderbit の AI Suggest Fields がページを読んで、自動で列名を提案します。列名の変更、項目追加、「compare_at_price が入っている商品だけ抽出して」のような指示もできます。

Python スクリプトがやっていることに近い機能として、次のようなものがあります。

  • サブページスクレイピング: 各商品詳細ページを自動で巡回し、説明文、レビュー、バリエーション詳細を表に補完します。Python でページを回しているのと同じことを、コードなしで実現します。
  • 自動ページネーション: クリック式のページ送りや無限スクロールも、設定なしで対応します。
  • 定期スクレイピング: 「毎週月曜9時」のような繰り返し実行を設定でき、継続的な価格監視に使えます。cron やサーバーは不要です。
  • CSV、Excel、Google Sheets、Airtable、Notion への無料エクスポート: すべてのプランで利用できます。

Python スクリプト vs Thunderbit:率直な比較

比較項目Python スクリプトThunderbit(ノーコード)
セットアップ時間15〜60分(環境構築 + コード)約2分(Chrome 拡張を入れるだけ)
コーディング必要(Python)不要
カスタマイズ性無制限AI 提案項目 + カスタムプロンプト
ページネーション対応自分で実装自動
出力形式自分で実装(CSV/Excel)CSV、Excel、Google Sheets、Airtable、Notion(無料)
定期実行cron + ホスティングが必要標準搭載のスケジューラ
レート制限対応リトライ / バックオフを実装自動処理
向いている人開発者、大規模データパイプラインビジネスユーザー、素早い抽出、定期監視

柔軟性や自作パイプラインとの統合が必要なら Python が向いています。すぐにデータが欲しくて、コードの保守をしたくないなら Thunderbit が最適です。価格比較のための Web スクレイピングについてさらに知りたい方は、別記事の も参考にしてください。

python-vs-thunderbit-comparison.webp

Shopify ストアをスクレイピングする際のヒントとベストプラクティス

どのツールを使う場合でも、次の点は共通です。

  • 必ず ?limit=250 を使う: 総リクエスト数を最小化できます。デフォルトの30件/ページのままだと、同じデータを取るのに8倍のリクエストが必要です。
  • ストアに配慮する: リクエスト間に1〜2秒の待機を入れましょう。高速連打はマナー違反であり、ブロックされる確率も上がります。
  • まず robots.txt を確認する: Shopify のデフォルト robots.txt/products.json をブロックしません。ただし、ストアごとの独自ルールもあるので、大量取得の前に確認してください。
  • まず生の JSON をローカル保存する: 先に保存しておけば、後で解析ロジックを変えても再スクレイピングが不要です。平坦化する前に json.dump(all_products, open("raw_data.json", "w")) しておくだけでかなり楽になります。
  • product.id で重複排除する: ページ境界では同じ商品が重複することがあります。df.drop_duplicates(subset=["product_id", "variant_id"]) で簡単に整理できます。
  • 価格は数値に変換してから計算する: Shopify の価格は数値ではなく文字列("185.00")で返ります。
  • エンドポイント変更を監視する: /products.json は長年安定していますが、将来的に制限される可能性はあります。突然 404 が出たら、まず手動で確認してください。

堅牢なスクレイパー構築については、 のガイドもどうぞ。

Shopify スクレイピング時の法的・倫理的な注意点

短くても重要な項目です。

/products.json エンドポイントが返すのは公開されている商品データで、ストアを普通に見ている一般ユーザーがアクセスできる情報と同じです。Shopify の利用規約には「Services」へのアクセスに自動化手段を使わない旨の文言がありますが、これは管理画面やチェックアウトなどのプラットフォーム本体を指し、公開ストアフロントのデータまでは通常含みません。2026年4月時点で、Shopify 特有のスクレイピング訴訟は提起されていません。

公開データのスクレイピングを支える重要な判例として、hiQ v. LinkedIn では公開情報の取得が CFAA に違反しないことが示され、Meta v. Bright Data(2024)では、利用規約上の制限はログイン中にのみ適用されると判断されました。

ベストプラクティス:

  • 公開されている商品データだけを取得する
  • 個人情報や顧客データは取らない
  • robots.txt とレート制限を守る
  • 個人データを扱う場合は GDPR / CCPA に準拠する(商品カタログデータは非個人情報)
  • 明確な User-Agent を付けて自分を名乗る
  • 自社 Shopify ストアを Admin API で取得するのは常に問題ない

さらに詳しく知りたい方は、 も見てみてください。

まとめと要点

Shopify の公開 /products.json エンドポイントを使えば、EC データの抽出は驚くほど手軽です。流れは、/products.json を付ける → Python で取得する → ?limit=250&page= でページを回す → pandas で整形する → CSV または Excel に出力する、というだけです。

このガイドが他と違うのは、次の点です。

  • 完全なフィールドリファレンス: コードを書く前に、商品・バリエーション・画像にまたがる40項目以上のデータを把握できる
  • 追加エンドポイント: /collections.json/meta.json により、他では扱われにくいカテゴリ分析やストアメタデータが取れる
  • 本番向け手法: セッション再利用、指数バックオフ、User-Agent ヘッダー、?limit=250 で実運用のレート制限に対応
  • 適切な CSV / Excel 出力: 生 JSON の保存だけでなく、pandas でバリエーション単位に平坦化して扱いやすくする
  • ノーコード代替: コードの柔軟性よりスピードを重視する人向けの

コードを書かずに単発でも継続でも Shopify データを取りたいなら、 を試してみてください。Shopify Scraper テンプレートがページネーションから出力まで全部やってくれます。多くのストアにまたがる大規模なデータパイプラインやカスタム処理が必要なら、このガイドの Python スクリプトがフルコントロールを提供します。

動画で学びたい方は をチェックしてください。関連トピックとしては、 も役立ちます。

Shopify スクレイピングに Thunderbit を試す

FAQ

products.json で任意の Shopify ストアをスクレイピングできますか?

多くの Shopify ストアはデフォルトでこのエンドポイントを公開しています。テストでは約71%で有効な JSON が返りました。ただし、カスタム設定や追加のセキュリティ層(Cloudflare、ヘッドレス構成など)があるストアでは、404 が返ったりリクエストが遮断されたりすることがあります。確認は簡単で、{store-url}/products.json をブラウザで開き、JSON が表示されればOKです。

Shopify ストアをスクレイピングするのは合法ですか?

公開されている商品データ(価格、商品名、画像、説明など)は一般にアクセス可能で、hiQ v. LinkedIn のような判例も公開情報のスクレイピングを支持しています。ただし、対象ストアの利用規約と自分の国・地域の法律は必ず確認してください。個人情報や顧客データは取らず、レート制限も守りましょう。

Shopify ストアから何件まで取得できますか?

総件数に明確な上限はありません。?limit=250&page= を使って、カタログ全体を取得できます。かなり大きいストア(2万5,000件超)では、レート制限を避けるためにセッション再利用と待機処理を入れてください。/meta.json を使えば、事前に正確な商品数を把握でき、必要ページ数も分かります。

products.json と Shopify Admin API の違いは何ですか?

/products.json は公開エンドポイントで、認証不要、読み取り専用の商品データを誰でも取得できます。Admin API はストア所有者のアクセストークンが必要で、注文、在庫数量、顧客データ、書き込み操作にも対応します。売上データや実在庫が必要なら Admin API が必要で、つまりストア所有者本人か、その許可を得ている必要があります。

Python なしで Shopify をスクレイピングできますか?

もちろん可能です。 のようなツールを使えば、Chrome 拡張からコードなしで Shopify を取得できます。ページネーションも自動で処理し、CSV、Excel、Google Sheets、Airtable、Notion に直接出力できます。別言語を使いたい開発者なら、同じ /products.json は JavaScript、Ruby、Go など、HTTP リクエストと JSON パースができる任意の言語で使えます。

さらに詳しく

Ke
Ke
CTO @ Thunderbit. Ke is the person everyone pings when data gets messy. He's spent his career turning tedious, repetitive work into quiet little automations that just run. If you've ever wished a spreadsheet could fill itself in, Ke has probably already built the thing that does it.
目次

Thunderbitを試す

リードや各種データをたった2クリックで取得。AI搭載。

Thunderbitを入手 無料です
AIでデータを抽出
Google Sheets、Airtable、Notionへ簡単にデータを転送
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week