2026年のShein大規模スクレイピング:プロキシ活用完全ガイド

Akamai Bot Managerを回避しながらSheinのカタログ・価格・在庫データを大規模に取得する実践ガイド。curl_cffiによるTLS指紋一致、住宅プロキシでのジオターゲティング、ページネーションとバックオフ戦略まで詳解。

Scraping Shein at Scale in 2026: A Practical Proxy Guide for Price Intelligence

2026年のShein大規模スクレイピングに取り組む価格インテリジェンスチームやファッション市場アナリストにとって、最初の技術的決断は「レンダリング済みHTMLのパースか、Sheinの内部JSON APIの利用か」です。SheinはReactベースのSPAであり、商品ページHTMLにはproductIntroDatagbProductDetailというJSON blobがインラインで埋め込まれています。一方、/api/productInfoやカテゴリ商品リストAPIのような内部エンドポイントも存在し、構造化されたJSONを直接返します。本記事では、Akamai Bot Managerを回避しながらSheinのカタログ・価格・在庫データを大規模に取得する実践的なアプローチを解説します。

2026年のShein大規模スクレイピング:API vs HTMLのトレードオフ

Sheinスクレイピングにおける最大の設計判断は、どのデータソースにアクセスするかです。以下の表は主な2つのアプローチを比較したものです。

観点 HTML内JSON blob抽出 内部APIエンドポイント
データ構造の安定性 中程度(ページ構造変更の影響を受ける) 低(パラメータ名が頻繁に変更される)
レスポンスサイズ 大きい(HTML全体を取得、約200KB) 小さい(JSONのみ、約5-20KB)
レイテンシ 約200-500ms(HTMLパース含む) 約100-200ms
必要なクッキー _abck, bm_sz, smdeviceid 同上+セッショントークン
フォールバック耐性 高い(複数の抽出ポイントが存在) 低い(エンドポイントが消えると即座に壊れる)

実務では両方を併用するのが定石です。商品詳細はHTML内のJSON blobから抽出し、カテゴリ一覧はAPIエンドポイントから取得します。片方が壊れてもう片方でフォールバックできる構成が最も堅牢です。Sheinプロダクトスクレイパーを構築する際は、このハイブリッド構成を前提に設計してください。

Sheinのアンチボットスタック:Akamai Bot Manager

SheinはAkamai Bot Managerを採用しており、複数の防御レイヤーでBotトラフィックを検知します。主要なコンポーネントは以下の通りです。

  • _abckクッキー: JavaScriptチャレンジを通過したブラウザのみに発行される検知トークン。無効な_abckクッキーでリクエストを送ると412ステータスが返されます。
  • bm_szクッキー: Bot Managerセッション識別子。_abckとペアで検証されます。
  • sensor_dataテレメトリ: マウス移動、キーボード入力、Canvas指紋、WebGL情報などのブラウザテレメトリを収集し、人間らしさを判定します。
  • smdeviceid: デバイス識別トークン。リクエストヘッダーに付与され、セッション間でデバイスを追跡します。

ヘッドレスChrome単体では、WebDriver検知フラグ、Canvas指紋の不一致、マウス移動パターンの欠如などによりBotと判定されます。PuppeteerのstealthプラグインやPlaywrightのstealthモードを使っても、Akamaiのsensor_data検査を完全に通過することは困難です。2026年現在、Akamaiの検知精度は継続的に向上しており、単純なブラウザ自動化では成功率が50%を下回るケースが増えています。

TLSフィンガープリンティングの重要性

AkamaiはTLSハンドシェイクの指紋も検査しています。Pythonのrequestsライブラリや標準的なHTTPクライアントのTLS指紋はChromeと異なるため、クッキーが有効でもリクエストがブロックされることがあります。ProxyHatのドキュメントでも推奨されているように、curl_cffiを使用してChrome互換のTLS指紋を送信するのが効果的です。

データの所在:JSON blob、SKU、在庫フィールド

Shein商品ページのHTMLソース内には、以下の構造化データが埋め込まれています。

商品詳細ページのJSON blob

ページHTML内の<script>タグにwindow.__INITIAL_STATE__またはproductIntroDataオブジェクトとして格納されています。主要フィールドは以下の通りです。

  • goods_id: 商品の一意識別子
  • retailPrice: 定価(現地通貨)
  • salePrice: セール価格(現地通貨)
  • stock: 在庫数(SKUレベル)
  • skuList: 各SKUのバリエーション情報(色、サイズ、在庫)
  • cat_id: カテゴリID

カテゴリフィードエンドポイント

商品リストはcat_idパラメータを指定したAPIエンドポイントから取得できます。例:/api/productInfo?goods_id=XXXX&cat_id=YYYY。このエンドポイントはJSON配列を返し、各要素にgoods_idretailPricesalePricestockが含まれます。

実務のヒント: cat_idはSheinのカテゴリツリーURLから抽出できます。例:/Women-Dresses-c-1727.htmlの場合、cat_id=1727です。

レート制限とプロキシローテーション戦略

SheinはIPあたりのリクエスト頻度に制限を設けており、観察ベースでは概ね1〜2リクエスト/秒/IPを超えると429または412ステータスが返されます。200リクエスト/分を超えるとIPブロックが発生する傾向があります。

さらに重要なのは、Sheinが通貨、価格、在庫を地域ごとにローカライズしている点です。米国IPで取得した価格とドイツIPで取得した価格は、通貨も金額も異なります。Shein価格データを正確に追跡するには、この地域差を考慮する必要があります。

このため、住宅プロキシでのIPローテーションとジオターゲティングが必須です。ProxyHatではユーザー名に-country-US-country-DEを指定するだけで、指定国の住宅IPを経由できます。

プロキシタイプの比較

プロキシタイプ Sheinでの成功率 レイテンシ 推奨用途
住宅プロキシ 高(90%以上) 200-800ms 商品詳細・価格取得(推奨)
データセンタープロキシ 低(30-50%) 50-150ms 非推奨(Akamaiが検知しやすい)
モバイルプロキシ 最高(95%以上) 300-1200ms 高難度シナリオ・少数リクエスト

価格・在庫データの大規模収集には、住宅プロキシがコストと成功率のバランスで最適です。ProxyHatの料金プランでは、住宅プロキシを従量制で利用できます。

Python実装例:curl_cffi + ProxyHatでShein価格データを取得

以下はcurl_cffiを使用してTLS指紋をChromeに一致させ、ProxyHatの住宅プロキシ経由でShein商品ページから価格・在庫データを抽出する実装例です。

from curl_cffi import requests
import json
import re

# ProxyHat住宅プロキシ(US地理位置指定)
proxy_url = "http://user-country-US:password@gate.proxyhat.com:8080"

def scrape_shein_product(goods_id: str) -> dict:
    url = f"https://www.shein.com/p-{goods_id}.html"
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/120.0.0.0 Safari/537.36",
        "Accept-Language": "en-US,en;q=0.9",
        "Accept": "text/html,application/xhtml+xml",
    }
    
    response = requests.get(
        url,
        headers=headers,
        proxies={"http": proxy_url, "https": proxy_url},
        impersonate="chrome120",
        timeout=15,
    )
    
    if response.status_code == 412:
        raise Exception("_abck challenge failed - rotate IP")
    
    # HTML内のproductIntroData JSON blobを抽出
    match = re.search(
        r'productIntroData\s*[:=]\s*(\{.*?\});',
        response.text,
        re.DOTALL
    )
    if not match:
        raise Exception("JSON blob not found in page")
    
    data = json.loads(match.group(1))
    return {
        "goods_id": data.get("goods_id"),
        "retailPrice": data.get("retailPrice"),
        "salePrice": data.get("salePrice"),
        "stock": data.get("stock"),
        "currency": data.get("currency", "USD"),
    }

result = scrape_shein_product("12345678")
print(json.dumps(result, indent=2))

サンプルレスポンス(truncated)

{
  "goods_id": "12345678",
  "retailPrice": {
    "amount": "29.99",
    "amountWithSymbol": "$29.99"
  },
  "salePrice": {
    "amount": "19.99",
    "amountWithSymbol": "$19.99"
  },
  "stock": 1247,
  "currency": "USD"
}

この例ではimpersonate="chrome120"を指定することで、TLS指紋をChrome 120に一致させています。これによりAkamaiのTLSフィンガープリンティング検査を通過しやすくなります。

curlコマンドラインでテストする場合は以下のように実行できます。

curl -x http://user-country-US:password@gate.proxyhat.com:8080 \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
  -H "Accept-Language: en-US,en;q=0.9" \
  "https://www.shein.com/p-12345678.html"

ページネーション、スティッキーセッション、バックオフ

ページネーション処理

カテゴリ一覧のページネーションはpageパラメータで制御します。以下は複数ページを順次取得する例です。

import time

def scrape_category(cat_id: str, max_pages: int = 10):
    all_products = []
    
    for page in range(1, max_pages + 1):
        url = "https://www.shein.com/api/category/products"
        params = {"cat_id": cat_id, "page": page, "page_size": 40}
        
        # 各ページで異なるセッションID → IPローテーション
        proxy = f"http://user-country-US-session-page{page}:password@gate.proxyhat.com:8080"
        
        resp = requests.get(
            url,
            params=params,
            proxies={"https": proxy},
            impersonate="chrome120",
            timeout=15,
        )
        
        if resp.status_code == 412:
            # _abckチャレンジ失敗 → IPを変えてリトライ
            continue
        
        products = resp.json().get("products", [])
        all_products.extend(products)
        
        # 1リクエスト/秒に制限
        time.sleep(1.0)
    
    return all_products

通貨一貫性のためのスティッキーセッション

同一商品の異なるSKUを取得する際、IPが変わると通貨表示が切り替わる可能性があります。ProxyHatの-session-フラグを使うと、同一セッションIDの間は同じIPを維持できます。

# スティッキーセッションで通貨一貫性を確保
session_id = "shein-product-12345"
proxy = f"http://user-country-DE-session-{session_id}:password@gate.proxyhat.com:8080"

# このセッションIDの間は常に同じドイツIPを使用
# 通貨はEURで一貫、価格もドイツ向けのものが返される

412/_abckリフレッシュとバックオフ

412ステータスを受信した場合は、_abckクッキーが無効化されています。以下の戦略で対応します。

  1. 即座にIPをローテーション: 新しい住宅IPに切り替えて新しい_abckクッキーを取得
  2. 指数バックオフ: 連続412の場合は1秒→2秒→4秒→8秒と待機時間を増加
  3. 最大3回リトライ: 3回失敗した場合はその商品をスキップし後で再試行
import time
import random

def fetch_with_retry(url: str, max_retries: int = 3) -> dict:
    for attempt in range(max_retries):
        # ランダムなセッションIDでIPをローテーション
        sid = f"retry-{attempt}-{random.randint(1000, 9999)}"
        proxy = f"http://user-country-US-session-{sid}:pass@gate.proxyhat.com:8080"
        
        try:
            resp = requests.get(
                url,
                proxies={"https": proxy},
                impersonate="chrome120",
                timeout=15,
            )
            
            if resp.status_code == 200:
                return resp.json()
            elif resp.status_code == 412:
                # 指数バックオフ
                wait = 2 ** attempt + random.uniform(0, 1)
                time.sleep(wait)
                continue
            elif resp.status_code == 429:
                time.sleep(5)
                continue
                
        except Exception:
            time.sleep(2 ** attempt)
    
    return None

倫理とTOS:公開データのみ、CFAA/GDPRの注意点

Sheinのスクレイピングを行う際は、以下の倫理的・法的ガイドラインを遵守してください。

  • 公開データのみ取得: ログイン不要でアクセスできる商品ページの価格・在庫データのみを対象とする
  • チェックアウトやアカウントアクセスを避ける: カート操作、購入フロー、ユーザーアカウントへのアクセスは行わない
  • robots.txtを尊重する: Sheinのrobots.txtで禁止されているパスにはアクセスしない
  • CFAA: 米国のComputer Fraud and Abuse Actに基づき、認証を回避する行為やサーバーに過度の負荷をかける行為は違法となる可能性がある
  • GDPR: 個人データを収集しない。商品データのみであればGDPRの対象外だが、レビュー投稿者のユーザー名や位置情報は個人データに該当する場合がある
  • リクエスト頻度の制御: サーバーに負荷をかけないよう、1IPあたり1リクエスト/秒以下を維持する

公式アフィリエイトフィードの検討: Sheinはアフィリエイトパートナー向けに商品データフィードを提供しています。商用利用が目的であれば、スクレイピングよりも公式API/フィードの利用を優先すべきです。スクレイピングは、公式フィードではカバーされないリアルタイム在庫変動や地域別価格差の追跡に限定するのが賢明です。

ProxyHat固有のセットアップと内部リソース

ProxyHatでSheinスクレイピングを開始する手順は以下の通りです。

  1. ProxyHatの料金プランから住宅プロキシプランを選択
  2. ダッシュボードでユーザー名とパスワードを確認
  3. ジオターゲティング付きでプロキシURLを構成: http://user-country-US:pass@gate.proxyhat.com:8080
  4. ウェブスクレイピングのユースケースを参照してベストプラクティスを確認
  5. 利用可能なロケーションで対象国がサポートされているか確認
  6. 必要に応じてSERPトラッキングと組み合わせて競合分析を実施

ProxyHatの全エンドポイントと設定オプションについては、公式ドキュメントを参照してください。SOCKS5プロキシが必要な場合はポート1080を使用します:socks5://user-country-US:pass@gate.proxyhat.com:1080

Key Takeaways

  • APIとHTMLの併用がベストプラクティス: 商品詳細はHTML内JSON blobから、カテゴリ一覧はAPIエンドポイントから取得し、フォールバック構成にする
  • Akamai Bot Managerは多層防御: _abckクッキー、sensor_dataテレメトリ、TLS指紋検査を組み合わせている。ヘッドレスChrome単体では不十分
  • curl_cffiでTLS指紋を一致させる: impersonate="chrome120"でChrome互換のTLSハンドシェイクを送信
  • 住宅プロキシ+ジオターゲティングが必須: Sheinは地域別に価格・通貨・在庫をローカライズするため、-country-US等で位置を固定
  • スティッキーセッションで通貨一貫性を確保: -session-フラグで同一IPを維持し、通貨切り替えを防ぐ
  • 412はIPローテーションで対処: 412受信時は即座に新しいIPに切り替え、指数バックオフで再試行
  • 公開データのみ、公式フィードも検討: 倫理と合法性を確保し、商用利用では公式アフィリエイトフィードを第一選択とする

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

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

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