Сбор данных о ценах на продукты: данные по магазинам и почтовым индексам в масштабе

Цены на продукты очень локальны: один и тот же товар стоит по-разному в зависимости от магазина и почтового индекса. Чтобы зафиксировать это, нужны гео-таргетированные запросы и сопоставление по UPC. Вот как создать набор данных о ценах на уровне магазина, который будет актуален.

Цены на продукты — один из самых локализованных наборов данных в интернете. Одна и та же коробка хлопьев может стоить $4.29 в одной сети и $5.19 в другой всего в нескольких милях, а платформы доставки предлагают разные цены в зависимости от магазина и установленного вами почтового индекса. Эта изменчивость делает данные ценными — для брендов CPG, отслеживающих розничное исполнение, для инструментов сравнения, для всех, кто измеряет инфляцию на полке. Но это также означает, что вы не можете просто собрать национальную цену; вам нужно фиксировать цены по каждому магазину и почтовому индексу, что является проблемой гео-таргетинга и сопоставления продуктов. Это руководство охватывает, как собирать данные о ценах на продукты на уровне магазина, сопоставлять товары по UPC и создавать индекс корзины, который будет актуален. Это общая информация, а не юридическая консультация — уважайте условия каждого сайта.

Почему разброс — это возможность

Цифры говорят сами за себя. Когда Consumer Reports заказал сравнение корзин по сетям в шести представительных городах США, разрыв между самым дешевым и самым дорогим обычным магазином в каждом городе превысил 33% — и стал еще больше, когда были включены клубы оптовой торговли и специализированные магазины. Цены на продукты выросли на 25.5% с декабря 2020 года по декабрь 2024 года по данным Бюро трудовой статистики, проанализированным Федеральным резервом Сент-Луиса, при этом кофе подорожал примерно на 20% в годовом исчислении. Еженедельные индексы, которые отслеживают это, собираются более чем из 150,000 магазинов. Набор данных, который фиксирует, кто самый дешевый, где и как это меняется из недели в неделю, действительно сложно собрать вручную — что делает его защищенным, когда вы автоматизируете его.

Местоположение контролирует цену, поэтому оно контролирует запрос

Основная техника: чтобы увидеть местные цены магазина, вы должны представиться покупателем в этом районе. На большинстве сайтов продуктов и доставки цены отображаются только после того, как вы установите почтовый индекс доставки или выберете конкретный магазин, и сайт привязывает это к вашей сессии и часто к местоположению вашего IP. Таким образом, запрос имеет две движущиеся части — магазин/почтовый индекс, который вы устанавливаете в полезной нагрузке, и выходной 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()
Панель статистики, показывающая разрыв в ценах на корзину в 33 процента, инфляцию цен на продукты в 25.5 процента и более 150000 магазинов в еженедельном индексе
Корзина может стоить на треть больше в одной сети, чем в другой в том же городе — фиксация этого разброса и есть вся суть.

Читайте JSON гидратации, а не отрисованную цену

Современные сайты продуктов сильно клиент-рендерятся, и — как и большинство крупных приложений электронной коммерции — они встраивают данные о продукте в виде 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)" — это один и тот же товар с тремя этикетками в трех магазинах — сопоставьте по названию, и ваше сравнение будет шумом. Сопоставляйте по штрих-коду 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

Масштабирование до реального набора данных

Поток данных о ценах на продукты — это матрица: магазины × почтовые индексы × продукты × время. Это много запросов, и здесь самодельный скрейпинг становится тяжелым — вы жонглируете сессиями, привязанными к гео, полезными нагрузками гидратации, которые меняются между развертываниями, и антибот-слоями на крупных сетях. Scraper API, который рендерит по необходимости, вращает гео-таргетированные IP и возвращает чистый JSON, сворачивает большинство этого в один вызов, так что вы тратите свои усилия на логику корзины, а не на сантехнику. Для стороны стратегии ценообразования тех же данных, смотрите наши руководства по мониторингу цен конкурентов и построению слоя данных для сравнения цен.

Диаграмма потока для сбора данных о ценах на продукты: установите магазин и почтовый индекс, извлеките JSON гидратации, сопоставьте по UPC, создайте индекс корзины
Установите местоположение, прочитайте JSON, который отправляет страница, сопоставьте по штрих-коду, затем сложите корзину по почтовому индексу со временем.

Часто задаваемые вопросы

Как собрать данные о ценах на продукты по местоположению?

Установите магазин или почтовый индекс доставки в запросе (обычно параметр или cookie) и направьте через резидентный прокси, чей выходной IP находится в этом регионе, чтобы сайт доверял местоположению и возвращал местные цены. Затем прочитайте цену и UPC из JSON гидратации страницы, а не из отрисованного элемента. Повторите по почтовому индексу, чтобы создать гео-матрицу.

Почему сопоставлять продукты по UPC, а не по названию?

Потому что магазины по-разному называют один и тот же товар — размеры, порядок брендов и сокращения варьируются — поэтому сопоставление по названию приводит к ложным несоответствиям и шумным сравнениям. Штрих-код UPC или GTIN одинаков у всех ритейлеров для одного и того же продукта, поэтому сопоставление по нему позволяет выровнять цены для точно такого же товара в каждом магазине, который вы скрейпите.

Действительно ли цены на доставку продуктов различаются по почтовому индексу?

Да. Платформы продуктов и доставки определяют цены, акции и доступность для магазина, а магазины привязаны к вашему почтовому индексу доставки, поэтому один и тот же продукт может показывать разные цены в соседних районах. Consumer Reports обнаружил разбросы корзин выше 33% между самыми дешевыми и самыми дорогими сетями в одном городе, поэтому сбор данных, специфичных для местоположения, имеет значение.

Является ли сбор данных о ценах на продукты законным?

Сбор общедоступных цен является обычным делом — новостные агентства и ценовые индексы делают это еженедельно — но это зависит от условий сайта и вашей юрисдикции. Придерживайтесь общедоступных страниц продуктов, избегайте личных данных и областей, требующих авторизации, соблюдайте темп запросов и получите юридическую консультацию для коммерческого использования. Это общая информация, а не юридическая консультация.

Данные о ценах на продукты живут и умирают на местоположении и идентичности. Установите магазин и почтовый индекс, представьтесь с резидентного IP в регионе, прочитайте JSON гидратации и сопоставьте по UPC — затем сложите корзину и отслеживайте ее со временем. Делайте это по достаточному количеству магазинов и почтовых индексов, и у вас будет набор данных, с которым ручное сравнение никогда не сможет сравниться.

Создайте свой поток данных о ценах на продукты с помощью Scraper API