食料品価格のスクレイピング:店舗および郵便番号レベルのデータを大規模に取得する方法
食料品の価格は非常にローカルです。同じ商品でも店舗や郵便番号によって価格が異なります。それを捉えるには、地理的にターゲットを絞ったリクエストとUPCのマッチングが必要です。ここでは、店舗レベルの価格データセットを構築する方法を紹介します。
食料品の価格はウェブ上で最もローカルなデータセットの一つです。同じシリアルの箱があるチェーンでは$4.29で、数マイル離れた別のチェーンでは$5.19になることがあります。また、配達プラットフォームは、設定した店舗や郵便番号によって異なる価格を提示します。この変動性こそがデータの価値を生む理由です。CPGブランドが小売実行を追跡するため、比較ツールのため、棚でのインフレを測定するために必要です。しかし、全国的な価格をスクレイピングするだけでは不十分で、店舗ごと、郵便番号ごとに価格を取得する必要があります。これは地理的ターゲティングと商品マッチングの問題です。このガイドでは、店舗レベルの食料品価格を収集し、UPCで商品をマッチングし、信頼性のあるバスケット指数を構築する方法を説明します。これは一般的な情報であり、法的アドバイスではありません。各サイトの利用規約を尊重してください。
なぜ価格差がチャンスなのか
数字がその理由を示しています。Consumer Reportsが6つの代表的な米国都市でチェーン間のバスケット比較を依頼した際、各都市で最も安い店舗と最も高い店舗の間の差は33%を超え、倉庫クラブや専門食料品店が含まれるとさらに広がりました。食品価格は2020年12月から2024年12月までに25.5%上昇し、コーヒーだけでも前年比約20%上昇しました。これを追跡する週次指数は15万以上の店舗からデータを引き出しています。誰が最も安いのか、どこで、週ごとにどのように変わるのかを捉えるデータセットは手作業で組み立てるのは非常に困難です。だからこそ、自動化すると防御力が高まるのです。
場所が価格を決定し、それがリクエストを制御する
基本的な技術:店舗のローカル価格を見るには、その店舗の地域の買い物客として振る舞う必要があります。ほとんどの食料品や配達サイトでは、配達の郵便番号を設定するか特定の店舗を選ぶと価格が表示され、その情報はセッションや多くの場合IPの位置に紐付けられます。したがって、リクエストには2つの可動部分があります。ペイロードに設定する店舗/郵便番号と、サイトが位置を信頼するために同じ地域にある出口IPです。ターゲットの都市圏にある住宅用IPが正しいローカル価格を解放し、他の州にあるデータセンターIPはデフォルトまたはブロックされたビューを得ることになります。
import httpx
# A residential exit in the target region unlocks that region's prices
def region_proxy(state):
return f"http://USER:PASS-country-us-region-{state}@gate.quantumproxies.io:8000"
HEADERS = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"accept": "application/json",
}
def fetch_store_price(store_api_url, zip_code, state):
# Many stores accept the ZIP as a cookie/param that scopes pricing
r = httpx.get(
store_api_url,
params={"zipCode": zip_code},
headers={**HEADERS, "cookie": f"store_zip={zip_code}"},
proxy=region_proxy(state),
timeout=30,
)
return r.json()

レンダリングされた価格ではなく、ハイドレーションJSONを読む
現代の食料品サイトはクライアントレンダリングが多く、ほとんどの大規模なeコマースアプリと同様に、製品データをページにJSONとして埋め込んでいます。ハイドレーションペイロード(<script>タグ内の__NEXT_DATA__や店舗固有の状態オブジェクトなど)を探し、価格、在庫状況、そして重要なUPCやGTINを含んでいるか確認します。そのJSONを読むことは、レンダリングされた価格要素をスクレイピングするよりもはるかに堅牢で、マッチングに必要なバーコードを提供します。スクレイパーが空のページを返す理由に関する私たちのノートでは、見えるHTMLが空に見えるときにこれらのペイロードを見つける方法を説明しています。
import re, json
def extract_hydration(html: str) -> dict:
m = re.search(r'<script id="__NEXT_DATA__"[^>]*>(\{.+?\})</script>',
html, re.DOTALL)
if not m:
raise ValueError("hydration payload not found")
return json.loads(m.group(1))
def parse_product(state_json: dict) -> dict:
p = state_json["props"]["pageProps"]["product"]
return {
"upc": p.get("upc") or p.get("gtin"),
"name": p.get("name"),
"price": p["price"]["current"],
"in_stock": p.get("availabilityStatus") == "IN_STOCK",
}
名前ではなくUPCでマッチングする
食料品価格データで最大の間違いは、商品を名前でマッチングすることです。「Cheerios Family Size」、「Cheerios Family Size 18oz」、「General Mills Cheerios (Family Size)」は、3つの店舗で3つのラベルが付けられた同じ商品です。名前でマッチングすると比較がノイズになります。UPC/GTINバーコードでマッチングすることで、各店舗が商品にどのようなタイトルを付けても同じです。真剣な食料品比較ツールはすべてこれを行います。これがクロスストアのバスケットを意味のあるものにするのです。
from collections import defaultdict
# Group scraped rows by barcode so each item lines up across stores
def compare_by_upc(rows: list[dict]) -> dict:
by_upc = defaultdict(dict)
for r in rows: # r = {store, upc, price, ...}
if r.get("upc"):
by_upc[r["upc"]][r["store"]] = r["price"]
# cheapest store per item
return {upc: min(prices, key=prices.get) for upc, prices in by_upc.items()}
バーコードでマッチングされた行から、重要な指標であるバスケット指数を構築できます。共通のアイテムの固定バスケットを定義し、各店舗ごとに郵便番号ごとに価格を設定し、合計します。この単一の数字を時間とともに追跡することで、33%の価格差や週ごとのインフレを明らかにします。多くの場所や店舗で同じ収集を行うため、住宅用プロキシプールを使用して各リクエストを回転させることで、設定した地理を維持しつつ、異常に見えることを防ぎます。
地理的にターゲットを絞ったIPでローカルの食料品価格を収集する
実際のデータセットへのスケーリング
生産用の食料品価格フィードは、店舗×郵便番号×商品×時間のマトリックスです。これは多くのリクエストを必要とし、DIYスクレイピングが重くなる部分です。地理的に制約されたセッション、デプロイ間で変動するハイドレーションペイロード、大手チェーンのアンチボット層を扱う必要があります。Scraper APIを使用すると、必要に応じてレンダリングし、地理的にターゲットを絞ったIPを回転させ、クリーンなJSONを返すことで、ほとんどの作業を1回の呼び出しにまとめることができます。これにより、配管作業ではなくバスケットロジックに集中できます。同じデータの価格戦略側については、競合価格監視と価格比較データレイヤーの構築に関するガイドをご覧ください。

よくある質問
どうやって場所ごとに食料品の価格をスクレイピングするのですか?
リクエストで店舗または配達の郵便番号を設定し(通常はパラメータまたはクッキー)、その地域にある住宅用プロキシを通じてルートを設定します。これにより、サイトが位置を信頼し、ローカル価格を返します。その後、レンダリングされた要素ではなく、ページのハイドレーションJSONから価格とUPCを読み取ります。郵便番号ごとに繰り返して地理的マトリックスを構築します。
なぜ食料品の商品を名前ではなくUPCでマッチングするのですか?
店舗は同じ商品に異なるタイトルを付けるためです。サイズ、ブランドの順序、略語がすべて異なるため、名前でのマッチングは誤った不一致やノイズの多い比較を生み出します。UPCまたはGTINバーコードは、同じ商品の場合、どの小売業者でも同一です。これにより、スクレイピングしたすべての店舗で正確に同じ商品の価格を揃えることができます。
食料品の配達価格は本当に郵便番号によって異なるのですか?
はい。食料品や配達プラットフォームは、価格、プロモーション、在庫を店舗に合わせて設定し、店舗は配達の郵便番号に結びついているため、同じ商品でも隣接する地域で異なる価格を表示することがあります。Consumer Reportsは、同じ都市内で最も安いチェーンと最も高いチェーンの間で33%以上のバスケット差を発見しました。これが、場所特有の収集が重要である理由です。
食料品の価格をスクレイピングすることは合法ですか?
公開されている価格を収集することは一般的です。ニュースメディアや価格指数は毎週これを行っていますが、それはサイトの利用規約とあなたの管轄によります。公開された商品ページに限定し、個人データやログインエリアを避け、リクエストのペースを守り、商業利用のためには法的アドバイスを受けてください。これは一般的な情報であり、法的アドバイスではありません。
食料品価格データは、場所とアイデンティティに依存しています。店舗と郵便番号を設定し、その地域の住宅用IPからアクセスし、ハイドレーションJSONを読み、UPCでマッチングします。その後、バスケットを合計し、時間をかけて追跡します。それを十分な店舗と郵便番号で行えば、手作業の比較では追いつけないデータセットを所有することになります。