PythonでShopifyストアをスクレイピングする方法

products.jsonと効果的なスクレイピング方法を活用することで、Shopifyのデータ抽出を簡素化します。
5 分読
How to Scrape Shopify blog image

額面通り、Shopifyストアはデータ抽出において最も難しい課題の1つです。下の商品は典型的なShopifyのリストです。データはまさに入れ子になっています。

<div class="site-box-content product-holder"><a href="/collections/ready-to-ship/products/the-eira-straight-leg" class="product-item style--one alt color--light   with-secondary-image " data-js-product-item="">

  <div class="box--product-image primary" style="padding-top: 120.00048000192001%"><img src="//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=640" alt="The Eira - Organic Ecru" srcset="//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=360 360w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=420 420w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=480 480w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=640 640w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=840 840w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=1080 1080w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=1280 1280w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=1540 1540w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=1860 1860w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=2100 2100w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=2460 2460w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=2820 2820w" sizes="(max-width: 768px) 50vw, (max-width: 1024px) and (orientation: portrait) 50vw, 25vw " loading="lazy" class="lazy lazyloaded" data-ratio="0.8" width="3200" height="4000" onload="this.classList.add('lazyloaded')"><span class="lazy-preloader " aria-hidden="true"><svg class="circular-loader" viewBox="25 25 50 50"><circle class="loader-path" cx="50" cy="50" r="20" fill="none" stroke-width="4"></circle></svg></span></div><div class="box--product-image secondary" style="padding-top: 120.00048000192001%"><img src="//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=640" alt="The Eira - Organic Ecru" srcset="//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=360 360w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=420 420w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=480 480w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=640 640w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=840 840w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=1080 1080w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=1280 1280w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=1540 1540w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=1860 1860w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=2100 2100w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=2460 2460w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=2820 2820w" sizes="(max-width: 768px) 50vw, (max-width: 1024px) and (orientation: portrait) 50vw, 25vw " loading="lazy" class="lazy lazyloaded" data-ratio="0.8" width="3200" height="4000" onload="this.classList.add('lazyloaded')"></div><div class="caption">

    <div>
      <span class="title"><span class="underline-animation">The Eira - Organic Ecru</span></span>
      <span class="price text-size--smaller"><span style="display:flex;flex-direction:row">$285.00</span></span>

    </div><quick-view-product class="quick-add-to-cart">
          <div class="quick-add-to-cart-button">
            <button class="product__add-to-cart" data-href="/products/the-eira-straight-leg" tabindex="-1">
              <span class="visually-hidden">Add to cart</span>
              <span class="add-to-cart__text" style="height:26px" role="img"><svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.57058 6.64336H4.49919C3.0296 6.64336 1.81555 7.78963 1.7323 9.25573L1.00454 22.0739C0.914352 23.6625 2.17916 25 3.77143 25H18.2286C19.8208 25 21.0856 23.6625 20.9955 22.0739L20.2677 9.25573C20.1844 7.78962 18.9704 6.64336 17.5008 6.64336H15.4294M6.57058 6.64336H15.4294M6.57058 6.64336V4.69231C6.57058 2.6531 8.22494 1 10.2657 1H11.7343C13.775 1 15.4294 2.6531 15.4294 4.69231V6.64336" stroke="var(--main-text)" style="fill:none!important" stroke-width="1.75"></path><path d="M10.0801 12H12.0801V20H10.0801V12Z" fill="var(--main-text)" style="stroke:none!important"></path><path d="M15.0801 15V17L7.08008 17L7.08008 15L15.0801 15Z" fill="var(--main-text)" style="stroke:none!important"></path></svg></span><span class="lazy-preloader add-to-cart__preloader" aria-hidden="true"><svg class="circular-loader" viewBox="25 25 50 50"><circle class="loader-path" cx="50" cy="50" r="20" fill="none" stroke-width="4"></circle></svg></span></button>
          </div>
        </quick-view-product></div><div class="product-badges-holder"></div></a></div>

上記のHTMLからデータを抽出することは不可能ではないが、もっと簡単な方法がある。

Shopifyランディングページ

https://hiutdenim.co.uk/、ランディングページには製品情報が掲載されているが、比較的限られている。十分に下にスクロールしてください。

Shopifyストアのフロントページ

一見すると、すべてのセクションへのすべてのリンクをスクレイピングし、その後、これらの異なるすべてのページを取得し、解析する必要があるように思えます。Shopifyストアはユニークなページレイアウトのため、eコマースのスクレイピングに関わる従来の方法には従わない。しかし、別の方法がある。

ShopifyのJSONページ

この見出しを正しく読んでください。デフォルトでストアの全商品をJSONオブジェクトとして取得できる。BeautifulSoupも Seleniumも必要ない。

URLに/products.jsonを追加するだけです。すべてのShopifyサイトはproducts.jsonファイルの上に構築されています。

ShopifyのJSONページ

このコンテンツをリクエストすることができれば(できるのだが)、私たちはありとあらゆるデータを手に入れることができる。データを手に入れたら、あとはどのデータを残すかを決めるだけだ。私たちが使っているサイトについては、ここで確認することができる。

PythonでShopifyをスクレイピングする

何を探しているのかがわかったので、この大変な作業もずっと難しくなくなった。扱うのはJSONデータだけなので、インストールが必要な依存関係はPython Requestsだけだ。

pip install requests

個々の機能

個々のコードを見てみよう。スクレーパーを構成する3つのチャンクがあります。

これが最も重要な機能だ。実際にスクレイピング・ロジックを実行する。

def scrape_shopify(url, retries=2):
    """scrape a shopify store"""
    json_url = f"{url}products.json"
    items = []
    success = False
    while not success and retries > 0:
        response = requests.get(json_url)
        try:
            response.raise_for_status()
            products = response.json()["products"]
            for product in products:
                product_data = {
                    "title": product["title"],
                    "tags": product["tags"],
                    "id": product["id"],
                    "variants": product["variants"],
                    "images": product["images"],
                    "options": product["options"]
                }
                items.append(product_data)            
            success = True
        except requests.RequestException as e:
            print(f"Error during request: {e}, failed to get {json_url}")
        except KeyError as key_error:
            print(f"Failed to parse json: {key_error}")
        except json.JSONDecodeError as e:
            print(f"json error: {e}")
        except Exception as e:
            print(f"Unforeseen error: {e}")
        retries-=1
        print(f"Retries left: ", retries)
    return items
  • まず、products.jsonをurlに追加します:json_url = f"{url}products.json"
  • 空の配列itemsを初期化します。アイテムをスクレイピングしながら、この配列に追加していきます。スクレイピングが終了したら、解析されたアイテムの配列を返します。
  • 良い反応がある限り、私たちは"products "キーを回収し、全商品を入手する。
  • 各商品から様々なデータを取り出し、product_dataという dictを作成します。
  • product_dataが配列に追加される。
  • このプロセスは、ページからすべての商品を解析するまで繰り返される。

スクレイピングを実行し、商品の配列を返す関数ができました。次に、この商品の配列を受け取ってファイルに書き出す関数が必要です。ここでCSVを使うこともできるが、この構造はかなり入れ子になるので、JSONを使うことにする。JSONは、後で使用したり分析したりするために、より柔軟なデータ構造をサポートしている。

def json2file(json_data, filename):
    """save json data to a file"""
    try:
        with open(filename, "w", encoding="utf-8") as file:
            json.dump(json_data, file, indent=4)
            print(f"Data successfully saved: {filename}")
    except Exception as e:
        print(f"failed to write json data to {filename}, ERROR: {e}")

これが実際に使うコードだ。さて、スクレーパーを実行するメインブロックを作成します。

if __name__ == "__main__":
    shop_url = "https://hiutdenim.co.uk/"
    items = scrape_shopify(shop_url)

    json2file(items, "output.json")

すべてをまとめる

すべてをまとめると、スクレイパーはこのようになる。複雑なパース・プロジェクトに思えたものが、今では約50行のコードで十分機能するスクレーパーになった。

import requests
import json

def json2file(json_data, filename):
    """save json data to a file"""
    try:
        with open(filename, "w", encoding="utf-8") as file:
            json.dump(json_data, file, indent=4)
            print(f"Data successfully saved: {filename}")
    except Exception as e:
        print(f"failed to write json data to {filename}, ERROR: {e}")

def scrape_shopify(url, retries=2):
    """scrape a shopify store"""
    json_url = f"{url}products.json"
    items = []
    success = False
    while not success and retries > 0:
        response = requests.get(json_url)
        try:
            response.raise_for_status()
            products = response.json()["products"]
            for product in products:
                product_data = {
                    "title": product["title"],
                    "tags": product["tags"],
                    "id": product["id"],
                    "variants": product["variants"],
                    "images": product["images"],
                    "options": product["options"]
                }
                items.append(product_data)            
            success = True
        except requests.RequestException as e:
            print(f"Error during request: {e}, failed to get {json_url}")
        except KeyError as key_error:
            print(f"Failed to parse json: {key_error}")
        except json.JSONDecodeError as e:
            print(f"json error: {e}")
        except Exception as e:
            print(f"Unforeseen error: {e}")
        retries-=1
    return items


if __name__ == "__main__":
    shop_url = "https://hiutdenim.co.uk/"
    items = scrape_shopify(shop_url)

    json2file(items, "output.json")

リターンデータ

データはJSONオブジェクトの配列で返されます。各商品はバリアントと 画像のリストを保持しています。これらをCSVで正確に表現するのはかなり難しいでしょう。下のスニペットは、スクレイピングで得られた1つの商品です。

{
        "title": "The Valerie - Organic Denim",
        "tags": [
            "The Valerie",
            "Women"
        ],
        "id": 14874183401848,
        "variants": [
            {
                "id": 54902462808440,
                "title": "UK10-29 / 30",
                "option1": "UK10-29",
                "option2": "30",
                "option3": null,
                "sku": null,
                "requires_shipping": true,
                "taxable": true,
                "featured_image": null,
                "available": true,
                "price": "220.00",
                "grams": 0,
                "compare_at_price": null,
                "position": 1,
                "product_id": 14874183401848,
                "created_at": "2025-01-21T14:04:58+00:00",
                "updated_at": "2025-02-12T17:17:54+00:00"
            },
            {
                "id": 54902462939512,
                "title": "UK12-30 / 32",
                "option1": "UK12-30",
                "option2": "32",
                "option3": null,
                "sku": null,
                "requires_shipping": true,
                "taxable": true,
                "featured_image": null,
                "available": true,
                "price": "220.00",
                "grams": 0,
                "compare_at_price": null,
                "position": 2,
                "product_id": 14874183401848,
                "created_at": "2025-01-21T14:04:58+00:00",
                "updated_at": "2025-02-12T17:17:54+00:00"
            },
            {
                "id": 54902463070584,
                "title": "UK14-32 / 28",
                "option1": "UK14-32",
                "option2": "28",
                "option3": null,
                "sku": null,
                "requires_shipping": true,
                "taxable": true,
                "featured_image": null,
                "available": true,
                "price": "220.00",
                "grams": 0,
                "compare_at_price": null,
                "position": 3,
                "product_id": 14874183401848,
                "created_at": "2025-01-21T14:04:58+00:00",
                "updated_at": "2025-02-12T17:17:54+00:00"
            },
            {
                "id": 54902463496568,
                "title": "UK18-36 / 30",
                "option1": "UK18-36",
                "option2": "30",
                "option3": null,
                "sku": null,
                "requires_shipping": true,
                "taxable": true,
                "featured_image": null,
                "available": true,
                "price": "220.00",
                "grams": 0,
                "compare_at_price": null,
                "position": 4,
                "product_id": 14874183401848,
                "created_at": "2025-01-21T14:04:58+00:00",
                "updated_at": "2025-02-12T17:17:54+00:00"
            }
        ],
        "images": [
            {
                "id": 31828166443078,
                "created_at": "2024-06-17T12:05:49+01:00",
                "position": 1,
                "updated_at": "2024-06-17T12:05:50+01:00",
                "product_id": 14874183401848,
                "variant_ids": [],
                "src": "https://cdn.shopify.com/s/files/1/0065/4242/files/HDC_0723_JapanInd_Valerie_45_3_c547ba8a-681b-4486-8cd7-884000e43302.jpg?v=1718622350",
                "width": 4000,
                "height": 4000
            },
            {
                "id": 31828166541382,
                "created_at": "2024-06-17T12:05:49+01:00",
                "position": 2,
                "updated_at": "2024-06-17T12:05:51+01:00",
                "product_id": 14874183401848,
                "variant_ids": [],
                "src": "https://cdn.shopify.com/s/files/1/0065/4242/files/HDC_0723_JapanInd_Valerie_Back_2_5909adb3-c2ab-4810-8b66-a486e8d827a8.jpg?v=1718622351",
                "width": 4000,
                "height": 4000
            },
            {
                "id": 31828166508614,
                "created_at": "2024-06-17T12:05:49+01:00",
                "position": 3,
                "updated_at": "2024-06-17T12:05:51+01:00",
                "product_id": 14874183401848,
                "variant_ids": [],
                "src": "https://cdn.shopify.com/s/files/1/0065/4242/files/HDC_0723_JapanInd_Valerie_Front_3_4316907a-9fd8-4649-894c-4028877370e1.jpg?v=1718622351",
                "width": 4000,
                "height": 4000
            },
            {
                "id": 31828166475846,
                "created_at": "2024-06-17T12:05:49+01:00",
                "position": 4,
                "updated_at": "2024-06-17T12:05:51+01:00",
                "product_id": 14874183401848,
                "variant_ids": [],
                "src": "https://cdn.shopify.com/s/files/1/0065/4242/files/HDC_0723_JapanInd_Valerie_Side_2_ea21477b-c1ba-4c8a-b75e-75c6427b4977.jpg?v=1718622351",
                "width": 4000,
                "height": 4000
            }
        ],
        "options": [
            {
                "name": "Waist",
                "position": 1,
                "values": [
                    "UK10-29",
                    "UK12-30",
                    "UK14-32",
                    "UK18-36"
                ]
            },
            {
                "name": "Leg Length",
                "position": 2,
                "values": [
                    "30",
                    "32",
                    "28"
                ]
            }
        ]
    },

高度なテクニック

複数のページをスクレイピングする必要があるかもしれないし、スクレイパーがブロックされることもあるかもしれない。

ページネーション

大きな店舗をスクレイピングしていると、ページネーションされた結果を持つ店舗に遭遇することがよくある。ページ分割を処理するには、まず、1ページあたりの最大結果を求めます。次のクエリパラメータを追加することができます:page=結果ページを制御するために。

URLとページ番号でページを取得するように、スクレイピング関数を少し修正することができる。

def scrape_shopify(url, retries=2):
    """scrape a shopify store"""
    json_url = f"{url}products.json"

そうすれば、その変化を反映させるためにメインを調整することができる。

if __name__ == "__main__":
    shop_url = "https://www.allbirds.com/"
    PAGES = 3

    for page in range(PAGES):
        items = scrape_shopify(shop_url, page=page+1)

        json2file(items, f"page{page}output.json")

プロキシの統合

スクレイパーがブロックされるのを防ぐためにプロキシサービスを使用する必要がある場合があります。私たちのShopifyプロキシを使えば、あなたの認証情報でURLを作成するだけで簡単にできます。

PROXY_URL = "http://brd-customer-<YOUR-USERNAME>-zone-<YOUR-ZONE>:<YOUR-PASSWORD>@brd.superproxy.io:33335"
proxies = {
    "http": PROXY_URL,
    "https": PROXY_URL
}
response = requests.get(json_url, proxies=proxies, verify="brd.crt")

ブライトデータのその他のソリューション

Bright Dataは、複雑なスクレイパーをゼロから構築する必要性をなくす、強力なターンキーオルタナティブを提供します。完全に最適化されたShopify Scraperを使用してシームレスなデータ抽出を行うか、複数のフォーマットで利用可能な収集済みデータセットの広範なライブラリにアクセスして、すぐにプロジェクトを開始することができます。

結論

Shopifyストアのスクレイピングは不可能な作業である必要はない。products.jsonを使ってAPIを活用するだけで、大量の詳細な商品データを素早く取得することができる。HTMLパーサーを使う必要もない!もし必要であれば、私たちのプリメイドスクレイパーを使って開発時間を短縮することもできますし、私たちのデータセットを使ってすぐに作業に取り掛かることもできます。

今すぐご登録ください!

クレジットカードは必要ありません