2026年版:PythonでWalmartをスクレイピングする方法(ブロックされないために)

最終更新日 July 22, 2026
2026年版:PythonでWalmartをスクレイピングする方法(ブロックされないために)

Walmartでは、一部商品の価格が1日に何度も変わります。価格や在庫をプログラムで継続取得する場合、スクリプトがしばらく正常に動作した後でも、HTTP 200のままCAPTCHAページが返されることがあります。ステータスコードだけで成功を判定すると、このサイレントブロックを見逃すおそれがあります。

Thunderbitでのデータ抽出業務の一環として、Walmartのボット対策にはかなり時間をかけて向き合ってきました。本記事では、その経験をもとに、実際に使える手法、データを汚染するサイレント失敗、自作スクレイパー・スクレイピングAPI・ノーコードツールのトレードオフを整理します。扱うのは、HTML解析、__NEXT_DATA__ JSON、内部APIのインターセプトという3つの抽出方法に加え、本番向けのエラーハンドリングと、要件に合う方法を選ぶための判断基準です。Pythonで実装する開発者と、短時間で価格一覧をスプレッドシートにまとめたい担当者の双方を対象にしています。

なぜPythonでWalmartをスクレイピングするのか?

Walmartは売上高ベースで世界最大の小売業者で、2025年度には6,809億8,500万ドルを記録し、Fortune Global 500で12年連続1位を維持しています。サイト上にはおよそ4億2,000万件の稼働中リスティングがあり、WalmartのCFOはマーケットプレイスについて「5億SKU」と述べています。その約95%はサードパーティ販売者によるもので、カタログは非常に流動的です。出品者は入れ替わり、バリエーションは変わり、在庫は毎日変動します。

walmart_stats_670d06c6bd.png

この変化の大きさこそ、スクレイピングが重要な理由です。四半期レポートでは、毎晩のスクレイピングで拾える細かな変化までは追えません。よくあるユースケースは次のとおりです。

用途必要とする人抽出対象
競合価格のモニタリングEC運営、価格改定ツール価格、プロモーション、MAP順守状況
商品カタログの充実営業、マーチャンダイジング担当説明文、画像、仕様、バリエーション
在庫有無の追跡サプライチェーン、ドロップシッパー在庫状況、販売者情報
市場調査・トレンド分析マーケティング、プロダクトマネージャー評価、レビュー、カテゴリ構成
リード獲得営業チーム販売者名、商品数、カテゴリ

競合価格モニタリングソフトウェア市場だけでも2025年に19.2億ドルに達し、2033年には50.9億ドルに成長すると予測されています。消費者行動もこの支出を後押ししています。オンライン購入者の94%が買い物中に価格を比較し、83%は複数サイトで見比べています。

この分野ではPythonが広く利用されています。Apifyの2026年インフラレポートによると、Webスクレイピング全体の71.7%がPythonで行われており、中核ライブラリのrequests週あたり約3億ダウンロードされています。スクレイピングの実装環境を選ぶ際、Pythonはライブラリや運用事例の多い有力な選択肢です。

Walmartのボット対策とスクレイピング時の主な課題

Walmartのスクレイピングが難しい主因として、2つの商用ボット対策製品を連続で使っている点が挙げられます。Akamai Bot ManagerがエッジWAFとTLSフィンガープリント層を担い、PerimeterX(現在はHUMAN Securityに改称)が挙動ベースのJavaScriptチャレンジ層を担っています。Scrape.doはこの組み合わせを「珍しく、回避が極めて難しい」と評しています。

walmart_antibot_3d67d0119c.png

ScrapeOpsはWalmartのスクレイピング難易度を総合9/10と評価しており、Akamai単体でも9/10です。実際の運用でも、複数の検知層を前提に設計する必要があります。

主な検知要素とデータ品質上の問題を整理すると、次のとおりです。

Akamai Bot Managerは、TLSフィンガープリント(JA3/JA4ハッシュ)、HTTP/2のフレーム順序、ヘッダーの順序と大文字小文字、セッションCookie(_abckak_bmsc)を検査します。標準のPython requests は実ブラウザと異なるTLSフィンガープリントを送るため、Walmartのアプリケーション層へ到達する前に遮断される場合があります。

PerimeterX/HUMANはAkamaiの後段で動き、JavaScriptによるフィンガープリント(px.js)を実行して、navigatorのプロパティ、canvas描画、WebGL、audio context、そして行動生体情報(マウス移動、スクロール速度、キー入力のリズム)を確認します。目に見える失敗例が「Press & Hold」チャレンジです。およそ10秒間ボタンを押し続け、その間に行動シグナルが採取されます。Oxylabsは率直にこう述べています。「WalmartはPerimeterX提供のCAPTCHAである『Press & Hold』方式を使っており、コードから解くのはほぼ不可能として知られている。」

運用上とくに注意したいのが、サイレントブロックです。Walmartは403ではなく、HTTP 200でCAPTCHA本文を返すことがあります。ScrapingBeeも確認しています。「WalmartはCAPTCHAページを返すときでも200 OKを返します。リクエストが成功したかどうかをステータスコードだけで判断することはできません。」CAPTCHAのHTMLを「商品が見つからない」と誤認したまま処理を続けると、欠損や誤った値がデータセットに混入しても検知できません。

さらに、店舗スコープのデータ問題があります。Walmartの価格と在庫は地域ごとに異なり、locDataV3assortmentStoreId のようなCookieで制御されています。適切なCookieがないと、「全国共通のデフォルト」データが返ります。一見すると完全に見えても、実際の購入者が見る内容とは一致しません。Cookie不足はブロックページではなく、見た目上は正常でも利用目的に合わないデータを返す可能性があるため、抽出後の照合が必要です。

Walmartからデータを抽出する3つの方法(比較つき)

実装手順の前に、主要な3つの抽出方法とノーコードの選択肢を比較します。必要なデータ項目、保守に使える時間、ボット対策を自社で扱うかどうかによって、適した方法は異なります。

方法信頼性データの完全性ボット対策の難易度保守負担
HTML + BeautifulSoup⚠️ 低い(レイアウト変更でセレクタが壊れる可能性がある)中程度高い高い
__NEXT_DATA__ JSON✅ 良い高い中〜高中程度
内部APIのインターセプト✅ 良い(詳細項目向け)高い(バリエーション、在庫、レビュー)中〜高中程度(構造化JSONと仕様変更への対応)
Thunderbit(ノーコード)✅ 良い高い比較的低い(AIが処理を支援)比較的低い

HTML解析は、レイアウト変更のたびにセレクタの見直しが発生しやすいため、Walmartでは保守負担が大きくなります。__NEXT_DATA__ JSON方式は、2024〜2026年の本格的なオープンソースWalmartスクレイパーが採用している実用的な方法です。内部APIのインターセプトは詳細な項目を取得したい場合の候補ですが、後述するPersisted Queryなどの保守が必要です。カスタムパイプラインを作らず、まず必要な列を表形式で取得したい場合は、Thunderbitも比較対象になります。

最初に代表的な商品ページで取得項目と出力形式を検証してから、運用規模に合う方法を選びます。

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

Python環境を整えてWalmartをスクレイピングする

必要なものは次のとおりです。

  • 難易度: 中級
  • 所要時間: セットアップに約30分、加えてコーディング時間
  • 必要なもの: Python 3.10以上、pip、コードエディタ、そして本番運用ではプロキシサービスまたはスクレイピングAPI

プロジェクトフォルダと仮想環境を作成します。

mkdir walmart-scraper && cd walmart-scraper
python -m venv venv
source venv/bin/activate  # Windowsの場合: venv\Scripts\activate

必要なライブラリをインストールします。

pip install curl_cffi parsel beautifulsoup4 lxml

curl_cffi は、2025年時点で難易度の高い対象をスクレイピングする際に使われる選択肢の一つです。ブラウザに近いTLSフィンガープリントを再現できるlibcurlバインディングで、Bright Dataの解説でも、「Walmartはボット検知の一部としてTLSフィンガープリントを使用しており、User-Agentを実ブラウザ風にしても回避できない」と説明されています。単なるrequestshttpxでは、ヘッダーだけを調整しても遮断される場合があります。本記事では、impersonate="chrome124" を指定したcurl_cffiを使って検証します。

このあと扱う本番向けのパターンのために、json(標準)、csv(標準)、timerandomloggingも使います。

PythonでWalmartの商品ページをスクレイピングする手順

ステップ1:Walmartの商品ページを取得する

最初に、Walmartの商品ページへHTTPリクエストを送り、正常な商品HTMLが返るかを判定します。2024〜2026年にScrapfly、Scrapingdog、Oxylabs、ScrapeOpsで共通して紹介されているヘッダー構成は次のとおりです。

from curl_cffi import requests

HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/124.0.0.0 Safari/537.36"
    ),
    "Accept": (
        "text/html,application/xhtml+xml,application/xml;q=0.9,"
        "image/avif,image/webp,*/*;q=0.8"
    ),
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Upgrade-Insecure-Requests": "1",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-User": "?1",
    "Referer": "https://www.google.com/",
}

session = requests.Session(impersonate="chrome124")
url = "https://www.walmart.com/ip/Apple-AirPods-Pro-2nd-Generation/1752657021"
response = session.get(url, headers=HEADERS)

ここで重要なのが impersonate="chrome124" です。この設定により、curl_cffi はChrome 124に近いTLS ClientHello、HTTP/2のフレーム順序、擬似ヘッダー順を再現します。設定しない場合、Pythonクライアント固有のフィンガープリントが検知され、Walmartのアプリケーション層へ到達する前に遮断される可能性があります。

ブロックされたレスポンスの見分け方: レスポンスHTMLのタイトルに "Robot or human?" が含まれる、または walmart.com/blocked にリダイレクトされる場合は、ブロックされた可能性があります。Walmartが200ステータスでCAPTCHA本文を返すケースもあるため、response.ok だけでなく本文も判定対象にします。

本番運用や繰り返し利用でブロックが続く場合は、住宅IPプロキシも選択肢になります。データセンターIPはIPレピュテーションによって遮断される場合があります。エラーハンドリングとプロキシ戦略は、後半の本番向けセクションで扱います。

ステップ2:__NEXT_DATA__ JSONから商品データを解析する

Walmart.comはNext.jsアプリケーションで、サーバーレンダリングされたHTMLの中に、単一のscriptタグとしてハイドレーションペイロードを埋め込んでいます。対象は<script id="__NEXT_DATA__" type="application/json">です。CSSセレクタだけで画面要素を追う方法より、構造化された商品データを取得しやすい場合があります。

Scrapflyの2026年版ガイドでも、「2026年のWalmartはNext.jsを使い、__NEXT_DATA__のscriptタグ内に構造化JSONを持っているため、隠しデータの抽出は従来のCSSセレクタ解析より信頼性が高い」と確認されています。Scrapfly版Oxylabs版python-scrapy-playbook版などのオープンソース実装でも、この方法が採用されています。

抽出方法は次のとおりです。

import json
from parsel import Selector

sel = Selector(text=response.text)
raw = sel.xpath('//script[@id="__NEXT_DATA__"]/text()').get()
data = json.loads(raw)
product = data["props"]["pageProps"]["initialData"]["data"]["product"]
idml = data["props"]["pageProps"]["initialData"]["data"].get("idml", {})

次の表は、実装時に使う主なJSONパスをまとめたものです。2024〜2026年のライブWalmartページで確認済みです。

データ項目JSONパス(initialData 配下)備考
商品名data > product > name文字列
ブランドdata > product > brand文字列
現在価格(数値)data > product > priceInfo > currentPrice > price浮動小数点数店舗Cookieで変わる場合あり
現在価格(文字列)data > product > priceInfo > currentPrice > priceString文字列例: "$9.99"
短い説明data > product > shortDescriptionHTML文字列テキスト化するならBeautifulSoupで解析
長い説明data > idml > longDescriptionHTML文字列product ではなく idml にある — 古いチュートリアルがよく間違える罠
すべての画像data > product > imageInfo > allImages配列{id, url} オブジェクトの一覧
平均評価data > product > averageRating浮動小数点数キーは旧式の rating ではなく averageRating
レビュー数data > product > numberOfReviews整数
バリエーションdata > product > variantCriteria配列サイズ、色などの選択肢グループ
在庫状況data > product > availabilityStatus文字列IN_STOCKOUT_OF_STOCKLIMITED_STOCK
販売者data > product > sellerDisplayName文字列
製造元data > product > manufacturerName文字列

longDescription のパスは、実装時に間違えやすい箇所です。2023年のScrapeHeroの投稿では product.longDescription に置かれていましたが、2024年以降のソースでは一貫して兄弟キーの idml にあります。まず idml.longDescription を読み、古いページ向けのフォールバックとして product.longDescription を使います。

.get() をつないで安全に抽出するパターンは次のとおりです。

def extract_product(data):
    product = data["props"]["pageProps"]["initialData"]["data"]["product"]
    idml = data["props"]["pageProps"]["initialData"]["data"].get("idml", {})

    price_info = product.get("priceInfo", {})
    current_price = price_info.get("currentPrice", {})
    image_info = product.get("imageInfo", {})

    return {
        "name": product.get("name"),
        "brand": product.get("brand"),
        "price": current_price.get("price"),
        "price_string": current_price.get("priceString"),
        "short_desc": product.get("shortDescription"),
        "long_desc": idml.get("longDescription", product.get("longDescription")),
        "images": [img.get("url") for img in image_info.get("allImages", [])],
        "rating": product.get("averageRating"),
        "review_count": product.get("numberOfReviews"),
        "variants": product.get("variantCriteria"),
        "availability": product.get("availabilityStatus"),
        "seller": product.get("sellerDisplayName"),
        "manufacturer": product.get("manufacturerName"),
    }

JSONパスを手作業で管理したくない場合は、ThunderbitのAIを使って項目候補を抽出し、表形式に整理する方法もあります。「AIで列を提案」をクリックすると、ページ内容に基づく列候補が表示されます。カスタムパイプラインを構築する場合は、上のパスマップを実装と検証の基準にできます。

ステップ3:Walmartの内部APIエンドポイントを確認し、詳細データを取得する

__NEXT_DATA__に含まれない項目が必要な場合は、内部APIの通信をDevToolsで確認する方法があります。取得できる項目は増えますが、Persisted Queryの更新などに対応する必要があり、実装と保守の難易度も上がります。

Walmartのフロントエンドは、Apollo Gateway上に構築されたFederated GraphQLバックエンドを呼び出しています。エンドポイントは www.walmart.com/orchestra/* 配下にあります。

  • /orchestra/pdp/graphql/... — 商品詳細のハイドレーションとバリエーション切り替え
  • /orchestra/snb/graphql/... — 検索・閲覧のページネーション
  • /orchestra/reviews/graphql/... — ページネーション付きレビュー

これらは、__NEXT_DATA__ では時に省略されるデータ、たとえばバリエーション単位の価格、リアルタイム在庫数、レビューの全ページなどを含む、構造化されたJSONを返します。

実装前に把握しておきたいのが、Walmartで使われているApolloのPersisted Queryです。リクエスト本文にはクエリ文字列ではなく、SHA-256ハッシュ(persistedQuery.sha256Hash)だけが送られます。そのハッシュがサーバーに未知なら、PersistedQueryNotFound が返ります。Walmartはデプロイのたびにこのハッシュをローテーションします。このため、固定した /orchestra/ コードを長期運用するのではなく、変更時に通信内容を取り直す手順が必要です。

DevToolsを使った調査手順は次のとおりです。

  1. ChromeでWalmartの商品ページを開く
  2. DevToolsを開き、Networkタブで「Fetch/XHR」に絞る
  3. 通常どおりページを操作する — バリエーションをクリックし、レビューまでスクロールし、店舗を変更する
  4. 商品データを返す /orchestra/* エンドポイントへのリクエストを探す
  5. 該当リクエストを右クリックして「Copy as cURL」
  6. そのcURLをcurl_cffiでPythonに変換する

再生したAPI呼び出しは、次のようになります。

import json
from curl_cffi import requests

session = requests.Session(impersonate="chrome124")

# まず商品ページを訪問してセッションを温める
session.get("https://www.walmart.com/ip/some-product/1234567", headers=HEADERS)

# その後、内部API呼び出しを再生する(DevToolsからコピーしたもの)
api_url = "https://www.walmart.com/orchestra/pdp/graphql"
api_headers = {
    **HEADERS,
    "accept": "application/json",
    "content-type": "application/json",
    "referer": "https://www.walmart.com/ip/some-product/1234567",
    "wm_qos.correlation_id": "コピーしたcorrelation_id",
}
payload = {
    # DevToolsから取得した正確なリクエスト本文を貼り付ける
    "variables": {"productId": "1234567"},
    "extensions": {
        "persistedQuery": {
            "version": 1,
            "sha256Hash": "コピーしたハッシュ"
        }
    }
}

api_response = session.post(api_url, headers=api_headers, json=payload)
api_data = api_response.json()

セッションを温める手順は非常に重要です。WalmartのPerimeterX Cookie(_px3_pxhdACID)は、最初のHTML取得でセットされていなければ、API呼び出しは成功しません。これがないと、412または403になります。

この方法を使うべき場面: __NEXT_DATA__ に含まれないデータ、たとえば深い階層のバリエーション価格、最初のバッチを超えるページネーション付きレビュー、リアルタイム在庫数が必要なときです。多くの用途では、__NEXT_DATA__ で十分であり、はるかに簡単です。

Walmartの検索結果と複数ページをスクレイピングする

検索結果も同様に __NEXT_DATA__ パターンですが、JSONパスは少し違います。

search_url = "https://www.walmart.com/search?q=laptops&page=1"
response = session.get(search_url, headers=HEADERS)

sel = Selector(text=response.text)
raw = sel.xpath('//script[@id="__NEXT_DATA__"]/text()').get()
data = json.loads(raw)

search_result = data["props"]["pageProps"]["initialData"]["searchResult"]
items = search_result["itemStacks"][0]["items"]

# スポンサード商品を除外する
organic_items = [i for i in items if i.get("__typename") == "Product"]

for item in organic_items:
    print(item.get("name"), item.get("priceInfo", {}).get("currentPrice", {}).get("price"))

ページネーションは page パラメータを増やしていく形です。&page=1&page=2 という具合です。ただし、文書化されていない上限があります。Walmartは、実際の総件数に関係なく検索結果を25ページまでに制限しています。 Scrapflyも確認しています。「Walmartは、利用可能な総ページ数に関係なく、アクセス可能な結果ページ数の上限を25に設定している。」

より深く取得するための回避策は次のとおりです。

  • 並び順の切り替え: 同じ検索クエリを &sort=price_low、次に &sort=price_high で実行し、約50ページ分をカバーする
  • 価格帯で分割: &min_price=X&max_price=Y を追加してカタログを小さな範囲に分ける
  • カテゴリで分割: サイト全体ではなく、特定カテゴリ内で検索する

itemStacks が配列である点にも注意してください。Scrapflyのリポジトリは [0] を固定で使っていますが、カテゴリページや閲覧ページには複数のスタック(「Top picks」「More results」など)が含まれることがあります。堅牢なパターンは、すべてのスタックを回すことです。

for stack in search_result.get("itemStacks", []):
    for item in stack.get("items", []):
        if item.get("__typename") == "Product":
            # item を処理する
            pass

あわせて重要なのは、Walmartのrobots.txt/searchを禁止していることです。商品詳細ページ(/ip/...)や多くのカテゴリページ(/cp/...)は禁止されていません。コンプライアンスが気になるなら、検索よりもまず商品ページやカテゴリツリーから始めましょう。

本番運用に必要なサイレントブロック検知とエラーハンドリング

1ページを取得して1商品を解析するだけのデモと、何千ページも継続取得する運用では、必要な設計が異なります。本番環境では、ブロックや不完全なレスポンスを正常データとして保存しないことが重要です。ここでは、失敗の検知、再試行、レート制限、データ検証、セッション監視を順に実装します。

サイレントブロックがデータを汚染する前に検知する

Walmartスクレイパーでは、ブロック検知が主要な処理の一つです。ScrapingBeeScrapingdogOxylabsDecodo の各社の知見を総合すると、独立した4つのチェックが必要です。

BLOCK_MARKERS = (
    "Robot or human",
    "Press &amp; Hold",
    "Press & Hold",
    "px-captcha",
    "perimeterx",
)

def is_walmart_blocked(response) -> bool:
    # 1. 専用ブロック先へのリダイレクト
    if "/blocked" in str(response.url):
        return True
    # 2. 明確なステータスコード
    if response.status_code in (403, 412, 428, 429, 503):
        return True
    # 3. 200 OKだがCAPTCHA本文のケース(サイレントブロック)
    body = response.text or ""
    if any(m.lower() in body.lower() for m in BLOCK_MARKERS):
        return True
    # 4. レスポンス長の健全性 — 正常なPDPは300〜900KB程度
    if len(response.content) < 50_000 and "/ip/" in str(response.url):
        return True
    return False

4つ目のチェックであるレスポンス長は、明らかなCAPTCHA文言はないのに、必要な商品データも入っていない、というケースを拾えます。

指数バックオフとジッターを使った再試行ロジック

リクエストが失敗した直後に再送を繰り返すと、対象サーバーへの負荷と追加のレート制限につながります。再試行間隔を段階的に延ばし、同時実行プロセスの再送時刻を分散させるため、指数バックオフにジッターを加えます。

import time
import random
import logging
from curl_cffi import requests as cffi_requests

log = logging.getLogger("walmart")

def fetch_with_retry(session, url, max_retries=5, base_delay=2, max_delay=60):
    for attempt in range(max_retries):
        try:
            response = session.get(url, headers=HEADERS, timeout=15)

            if response.status_code in (429, 503):
                raise Exception(f"Throttled: {response.status_code}")

            if is_walmart_blocked(response):
                raise Exception("Silent block detected")

            return response

        except Exception as e:
            if attempt == max_retries - 1:
                raise

            wait = min(max_delay, base_delay * (2 ** attempt)) + random.uniform(0, 3)
            log.warning(f"Attempt {attempt + 1} failed: {e}. Retrying in {wait:.1f}s")
            time.sleep(wait)

    return None

ジッター(random.uniform(0, 3))により、複数のスクレイパーが同じ秒に再試行する事態を避け、リクエストの集中を抑えます。

レート制限

ThunderbitScrape.doは、Walmartに対して1リクエストあたり3〜6秒のランダム遅延という点で一致しています。「ページ読み込みの間隔を3〜6秒空け、遅延をランダム化せよ。」ということです。

import time
import random

def rate_limited_fetch(session, url):
    response = fetch_with_retry(session, url)
    time.sleep(random.uniform(3.0, 6.0))
    return response

大規模運用では、非同期レート制限に aiolimiter を検討してください。

from aiolimiter import AsyncLimiter
limiter = AsyncLimiter(max_rate=10, time_period=60)  # 1分あたり10リクエスト

データ検証

レスポンスがブロックされていなくても、解析結果が間違っていることがあります(店舗違い、劣化したペイロードなど)。出力に書き込む前に検証してください。

def validate_product(product):
    """商品データが妥当ならTrueを返す。"""
    if not product.get("name"):
        return False

    price = (product.get("priceInfo") or {}).get("currentPrice", {}).get("price")
    if not isinstance(price, (int, float)) or price <= 0:
        return False

    if product.get("availabilityStatus") not in ("IN_STOCK", "OUT_OF_STOCK", "LIMITED_STOCK"):
        return False

    return True

セッションログ

セッションごとの成功率を追跡してください。10分間で80%を下回ったら、何かが変わったサインです。IPが焼かれたか、Cookieが切れたか、Walmartが新しいボット対策を導入した可能性があります。

class ScrapeMetrics:
    def __init__(self):
        self.total = 0
        self.success = 0
        self.blocks = 0
        self.errors = 0

    def record(self, result):
        self.total += 1
        if result == "success":
            self.success += 1
        elif result == "blocked":
            self.blocks += 1
        else:
            self.errors += 1

    @property
    def success_rate(self):
        return (self.success / self.total * 100) if self.total > 0 else 0

    def check_health(self):
        if self.total > 20 and self.success_rate < 80:
            log.critical(f"Success rate dropped to {self.success_rate:.1f}% — consider rotating proxies or pausing")

この監視は、誤ったレスポンスを保存し続けないために必要です。

要件別に選ぶ:自作Python vs. スクレイピングAPI vs. ノーコード

実装を始める前に、必要な件数、予算、エンジニアリング余力、データ項目、運用期間を整理します。ScrapeOpsはWalmartを難易度9/10と評価しています。自作が適するか、外部サービスに任せるかは、これらの条件によって変わります。

要素自作Python(requests + プロキシ)スクレイピングAPI(Oxylabs、Bright Dataなど)ノーコードツール(Thunderbit
最初の1行までのセットアップ時間数時間15〜60分約2分
本番投入までのセットアップ時間40〜80時間4〜16時間約30分
ボット対策対応自分で対応(難しい)ベンダーが対応ツール側が支援
小規模時のコスト(月1,000ページ未満)低い(プロキシ代は約4〜8ドル/GB)月額40〜49ドルの入門プラン無料〜月15ドル
大規模時のコスト(月10万ページ以上)リクエスト単価は低めリクエスト単価は高め変動
カスタマイズ性完全自由APIパラメータ次第UI/列に制約あり
継続保守月4〜8時間ベンダー対応が中心比較的少ない(対象ページや条件による)
最適な用途独自パイプラインを作る開発者中規模の本番スクレイピングビジネスユーザー、単発の素早い抽出

自作Pythonが向いているケース

自作Pythonが候補になるのは、すでにプロキシ契約を持っている場合、ヘッダーや郵便番号ターゲティング、販売者セグメントを厳密に制御したい場合、月間数百万ページを索引化していてAPIの従量課金が積み上がる場合、オンプレミスやコンプライアンス保証が必要な場合です。一方で、ページネーション、再試行、プロキシローテーション、TLS偽装、複数のページタイプのスキーマ対応を自社で実装・保守する工数が発生します。本番レベルのScrapyスパイダーには、シニアPythonエンジニアの40〜80時間に加え、Walmartがフィンガープリントを更新するたびに月4〜8時間の保守が必要です。

スクレイピングAPIが向いているケース

スクレイピングAPIは、ボット対策層の実装と運用をベンダー側に寄せたい場合の選択肢です。ScrapeOpsのベンチマークでは、Walmartに対してZyte APIが99% 、**Scrape.doが98%**の成功率を示しています。 ScraperAPIOxylabsScrapingdog のようなツールは、入門プランが月40〜49ドルです。2〜5人規模のエンジニアチームで、月1万〜100万ページを扱い、自社保守を抑えたい場合は、APIが候補になります。リクエスト単価と引き換えに、ボット対策やプロキシ運用の負担を減らせます。

ノーコードが向いているケース

Thunderbit は、PM、アナリスト、EC運営担当など、開発スプリントを組まずにWalmartの商品データをスプレッドシートへまとめたい利用者に向いています。

使い方は次のとおりです。ThunderbitのChrome拡張機能を入れ、Walmartの商品ページまたは検索ページを開き、「AIで列を提案」をクリックします。ThunderbitのAIがページを読み取り、商品名、価格、評価などの列を提案します。「スクレイピング」をクリックすると、データが表に入ります。Excel、Google Sheets、Airtable、Notionへエクスポートでき、無料枠で試せる範囲もあります。

Thunderbitはクラウド側でボット対策を処理し、CAPTCHA、プロキシ、TLSフィンガープリントへの対応をユーザー側で個別実装する負担を減らします。AIによるレイアウト変更への対応も、手動保守を抑えたい場合に役立ちます。ただし、対象ページや取得条件によって結果は異なるため、代表的なページで列と値を検証してから運用範囲を広げます。

制約として、Thunderbitは1日10万ページ超の運用向けには作られていません。クレジット予算とクラウド上限のため、大量取り込みでは生のAPIより非経済的です。対応していない場合は、特定の郵便番号やASNを固定することもできません。継続的な大規模パイプラインなら、自作かスクレイピングAPIが適しています。

ざっくりした価格感: ThunderbitでWalmartの商品行を1,000件取得するには、約2,000クレジット(Starter/Proプランでおよそ0.60〜1.10ドル)です。これはOxylabsのWalmart APIと同程度で、少量利用なら多くの趣味レベルのスクレイピングAPIより安いです。最新情報はThunderbitの料金ページをご確認ください。

AIでWalmartの商品データをスクレイピング Get Started Free

抽出したWalmartデータを書き出す

データを取ったら、使いやすい場所に置く必要があります。多くの用途は次の3形式で足ります。

CSV — アナリストが実際に開く、最も汎用的な形式です。

import csv

def export_csv(products, filename="walmart_products.csv"):
    fieldnames = ["name", "price", "availability", "rating", "review_count", "seller", "url"]
    with open(filename, "w", newline="", encoding="utf-8-sig") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames, quoting=csv.QUOTE_MINIMAL)
        writer.writeheader()
        for p in products:
            writer.writerow({k: p.get(k) for k in fieldnames})

Excel互換のために utf-8-sig を使ってください。BOMマーカーがあると、Excelが特殊文字を崩しにくくなります。

JSONL — スクレイピングパイプライン向けの本番形式です。

import json
import gzip

def export_jsonl(products, filename="walmart_products.jsonl.gz"):
    with gzip.open(filename, "at", encoding="utf-8") as f:
        for p in products:
            f.write(json.dumps(p, ensure_ascii=False) + "\n")

JSONLはクラッシュに強く(途中で止まっても失うのは最後の1行だけ)、一定メモリでストリーミングでき、バリエーションやレビューのようなネストしたデータもそのまま保持できます。

Excel — 単発の引き継ぎ向けです。

from openpyxl import Workbook

def export_excel(products, filename="walmart_products.xlsx"):
    wb = Workbook(write_only=True)
    ws = wb.create_sheet("Products")
    ws.append(["Name", "Price", "Availability", "Rating", "Reviews", "Seller"])
    for p in products:
        ws.append([p.get("name"), p.get("price"), p.get("availability"),
                    p.get("rating"), p.get("review_count"), p.get("seller")])
    wb.save(filename)

Pythonを使わない場合は、ThunderbitからGoogle Sheets、Airtable、Notion、Excel、CSV、JSONへ出力できます。利用できる出力先と無料枠はプラン条件に従います。継続監視では、Thunderbitのスケジュール済みスクレイパー機能も選択肢になります。

スケジューリング時は、WalmartのスクレイピングにGitHub Actionsは使わないでくださいという検証記事も参考になります。GitHub ActionsのランナーはAzureのIP帯域にあるため、Walmart側でブロックされる可能性があります。VPS上でAPSchedulerを使う方法、または住宅プロキシ経由で通信する方法を、運用要件に応じて比較します。

Walmartスクレイピングの法的・倫理的ガイドライン

技術的にデータを取得できることと、契約上・法的に許容されることは別です。対象データ、利用目的、取得規模、アクセス方法を整理したうえで、利用規約と関連法令を確認する必要があります。

Walmartの利用規約は、明示的に「事前の書面による明示的な同意」なしに、いかなるロボット、スパイダー、その他の手動・自動デバイスを使って「資料を取得、索引化、『スクレイプ』、『データマイニング』、またはその他の方法で収集すること」を禁じています。

Walmartのrobots.txtは、/search/account/api/、そして多数の内部エンドポイントを禁止しています。商品詳細ページ(/ip/...)とレビュー(/reviews/product/)は禁止されていません。

hiQ対LinkedIn判決(第9巡回、2022年)は、一般公開データのスクレイピングが連邦CFAAに違反する可能性は低いとしました。ただし同じ裁判では、その後hiQがLinkedInの利用規約に違反したと判断され、50万ドルの同意判決が出ています。さらに2024年のより新しい判断(Meta対Bright DataX Corp.対Bright Data)では、CFAAの適用はさらに狭められ、著作権による先占の抗弁も生まれましたが、これらはWalmartにそのまま当てはまるわけではない、個別の利用規約文言に依存した判断でした。

実務上の指針: サーバーに過度な負荷をかけず、アクセス頻度を制御します。個人情報やユーザーデータは取得対象から除外し、取得したデータの利用目的と保管方法を記録します。公開されているWalmartの商品ページを個人研究の範囲で適度な速度でスクレイプする場合と、Walmartの利用規約に反して商用規模で抽出する場合では、リスクが異なります。Walmartデータを使った製品を作るなら、弁護士に相談し、Walmartの公式なアフィリエイトAPIと販売者APIも確認してください。

免責事項: これは教育目的の情報であり、法的助言ではありません。

まとめと重要ポイント

WalmartをPythonでスクレイピングする場合は、AkamaiとPerimeterXによるボット対策、サイレントブロック、店舗スコープの違いを前提に設計する必要があります。難易度9/10のチャレンジという評価もあり、抽出コードだけでなく、失敗検知とデータ検証まで含めて方法を選ぶことが重要です。

重要ポイント:

  • __NEXT_DATA__ JSON抽出は、ほとんどの用途で実用的な候補です。2024〜2026年の本格的なオープンソースWalmartスクレイパーはすべてこれを使っています。基本パスは、PDPでは props.pageProps.initialData.data.product、検索・閲覧では searchResult.itemStacks です。
  • impersonate="chrome124" を指定した curl_cffi は、TLSフィンガープリント対策を検証する際の主要な選択肢です。通常のrequestshttpxでは、ヘッダーを調整しても遮断される場合があります。
  • サイレントブロックはデータ品質に直接影響します。 WalmartはCAPTCHA本文つきで200 OKを返すため、ステータスコードだけでなくレスポンス本文も判定します。
  • 本番運用では、正常時の抽出コードに加えて障害処理が必要です。指数バックオフとジッター、4つのシグナルによるブロック検知、1リクエストあたり3〜6秒のレート制限、データ検証、セッションの健全性監視を組み合わせます。
  • /orchestra/* 経由の内部APIのインターセプトは詳細データ向けですが、仕様変更への対応が必要です。主な抽出方法ではなく、特定データが必要なときのDevTools調査として使ってください。
  • Walmartは検索結果を25ページで打ち切ります。 並び順の切り替えや価格帯分割で広くカバーしましょう。
  • 手法は運用条件で選びます: 独自要件と大量処理がある開発者には自作Python。スクレイピング専任エンジニアがいない中規模チームにはスクレイピングAPI。短時間でGoogle SheetsにデータをまとめたいビジネスユーザーにはThunderbitが候補になります。

ノーコードで試す場合は、ThunderbitのChrome拡張機能の無料プランでWalmartのページを数件取得し、列、値、出力形式を照合してから対象を広げます。Pythonで進める場合、この記事のコードパターンは本番で検証済みです。ただし、まず代表ページでブロック検知とデータ検証のログを取り、想定した品質を満たすか確かめてから定期運用へ移します。

Webスクレイピングの手法についてさらに学ぶなら、Webスクレイピングの手順:Pythonで簡単にウェブスクレイピングする方法おすすめの自動WebスクレイピングツールブロックされずにWebスクレイピングする方法のガイドもぜひご覧ください。ThunderbitのYouTubeチャンネルではチュートリアル動画も公開しています。

FAQ

Walmartの商品データをスクレイピングするのは合法ですか?

Walmartの利用規約では、書面による同意なしの自動スクレイピングを禁じています。第9巡回区の hiQ対LinkedIn 判決(2022年)は、公開ページのスクレイピングには連邦CFAAが適用されにくいことを示しましたが、同じ事件は最終的にスクレイパー側に対する50万ドルの契約違反判決で終わっています。個人研究のために公開商品ページを控えめな速度でスクレイプするのと、商用規模で抽出するのとでは、リスクの大きさがまったく違います。Walmartデータを使って事業を作るなら、弁護士に相談してください。

なぜWalmartスクレイパーはブロックされ続けるのですか?

よくある原因として、通常のrequestshttpxが実ブラウザと異なるTLSフィンガープリントを送っていること、ヘッダーの不足または誤り、プロキシのローテーションがないこと、1ページあたり3〜6秒より短い間隔でリクエストしていること、セッションCookie(_px3_abcklocDataV3)が不足していることが挙げられます。まず本文とリダイレクト先を記録して原因を切り分け、そのうえでimpersonate="chrome124" を指定した curl_cffi、住宅プロキシ、この記事で説明したブロック検知と再試行パターンを、必要な範囲で検証します。

PythonでWalmartからどんなデータを取れますか?

商品名、価格(現在価格と値下げ前価格)、画像、短い説明と長い説明、評価、レビュー数、在庫状況、販売者名、製造元情報、バリエーション選択肢(サイズ、色)、カテゴリ上の位置づけです。__NEXT_DATA__方式なら、これらはすべて構造化JSONとして取得できます。内部APIのインターセプトを使えば、さらにバリエーション単位の価格、リアルタイム在庫数、ページネーション付きレビューも取得できます。

Walmartをスクレイピングするのにプロキシは必要ですか?

本番運用や繰り返し取得では、プロキシが必要になるケースが多くあります。Walmartのボット対策は通常のリクエストを一貫してブロックしますという検証もあり、ヘッダーを調整しても、IPレピュテーションによって遮断される場合があります。住宅プロキシまたはモバイルプロキシを候補にし、対象ページとリクエスト量で成功率を比較します。データセンターIPは短時間で使えなくなる場合があります。プロバイダとプランにもよりますが、1,000ページあたりおよそ3〜17ドルを見込んでください。

コードを書かずにWalmartをスクレイピングできますか?

はい。ThunderbitはAI搭載のChrome拡張機能で、2クリックでWalmartをスクレイピングします。「AIで列を提案」で商品データ列を自動検出し、「スクレイピング」でデータを抽出します。ボット対策はクラウド側で処理され、Excel、Google Sheets、Airtable、Notionへ直接エクスポートできます。無料枠で代表的な商品ページを試し、列、値、出力形式が用途に合うか検証できます。独自パイプラインを作らず、短時間でデータが必要なアナリスト、PM、ビジネスユーザーに向いています。大量・高度にカスタマイズしたスクレイピングには、PythonかスクレイピングAPIも比較対象になります。

AIでWalmartをスクレイピングするならThunderbitを試す Get Started Free

詳しくはこちら

Shuai Guan
Shuai Guan
Thunderbit の CEO | AIデータ自動化のエキスパート Shuai Guan は Thunderbit の CEO であり、ミシガン大学工学部の卒業生です。テクノロジーと SaaS アーキテクチャの分野で約10年にわたる経験をもとに、複雑な AI モデルを、実務で使えるノーコードのデータ抽出ツールへと落とし込むことを得意としています。このブログでは、ウェブスクレイピングや自動化戦略について、実践で磨かれた率直な知見を共有し、より賢くデータ主導のワークフローを構築できるよう支援しています。データワークフローの最適化から離れているときは、同じこだわりと観察眼を写真への情熱にも注いでいます。
目次

ただ伝えるだけで、Webページをスクレイピング

必要なことをそのまま英語で伝えるだけ。いや、何も言わなくてもOKです。

Thunderbitを試す 無料
AIでデータを抽出
Google Sheets、Airtable、Notionへ簡単にデータを移行できます
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week