Дузина яйца Lucerne в един магазин Safeway във Вашингтон се предлагаше в Instacart на цени $3.99, $4.28, $4.59, $4.69 и $4.79. Един и същ магазин, един и същ момент, различни купувачи. Кое от тези пет числа принадлежи към вашия ценови панел?
Предизвикателството
При хранителните стоки ценовото разузнаване спира да бъде просто "изтегли страницата на продукта, парсирай цената." Цената на хранителните стоки принадлежи на конкретен магазин, често на зона за доставка, и все по-често на този, който я гледа.
Започнете с магазина. В ръководството за сравнение на цени на хранителни стоки на Scrapfly един галон мляко струва $3.98 в един Walmart и $4.29 в друг на 20 мили разстояние, като се предупреждава, че сесия без локация на магазина получава данни по подразбиране, които може да не съответстват на нито един реален магазин. В ръководството за доставка на хранителни стоки за 2026 г. на ScrapeInsight се вижда същото едно ниво по-надолу: $3.99 в една зона за доставка, $4.49 на няколко мили разстояние. В казуса за ShopGrok на Bright Data се описва как ценообразуването в австралийската търговия на дребно става зависимо от пощенския код през почти четирите години дейност на компанията.
След това идва купувачът. През декември 2025 г. Groundwork Collaborative, Consumer Reports и More Perfect Union тестваха 437 купувачи в реални условия в четири града, пълнейки идентични колички в Instacart от същите магазини по едно и също време. 74% от артикулите се появиха на повече от една цена. Там, където цените се различаваха, разликата между най-ниската и най-високата беше средно 13%, а целите кошници варираха с около 7%.
Обединете тези фактори и ще получите грешката, която прави данните за хранителни стоки скъпи. Губите контекста на магазина и нищо не се чупи. Сайтът не връща грешка. Той връща напълно валидна цена за магазин, за който не сте питали, със SKU, число и клеймо за време, които преминават всяка ваша проверка за null.
Вече писахме за случая, при който блокирането изглежда като точка с данни. Този случай се улавя по-трудно, защото страницата наистина е страница с цени. Просто не е вашата.
Докажете магазина в самия отговор
Повечето колектори изпращат избора на магазин (cookie, параметър в заявката, header) и му се доверяват. По-надеждният навик е да накарате отговора да го докаже. Ако страницата посочва магазина, за който е рендирана, например ID на магазина във вградените данни или име на магазина в банера за вземане на място, този маркер става вашето определение за успех. В FourA това е едно извикване на Proxy Finder:
import requests
r = requests.post(
"https://api.foura.ai/api/proxy",
headers={"X-API-Key": "pk_live_..."},
json={
"maxTries": 6,
"request": {
"method": "GET",
"url": "https://grocer.example/product/0001234",
"headers": [["Cookie", "store=1234"]],
"validate": {
"status": {"accept": [200]},
"data": {
"accept": ["\"storeId\":\"1234\""],
"fail": ["Just a moment", "Access Denied"]
}
}
}
}
).json()
if "error" in r:
report = r.get("attemptReport", {})
print(report.get("summary", r["error"]))
if report.get("contentRejected", 0) > report.get("defense", 0) + report.get("noResponse", 0):
print("the site answered for another store: check the store selection")
else:
page, exit_id = r["data"], r["proxy"]
Три детайла в тази заявка лесно могат да бъдат объркани.
accept преминава успешно, когато който и да е от низовете в него се появи на страницата. Ако поставите маркер за цена до маркера за магазин, страница за грешен магазин ще премине успешно, защото тя също съдържа цена. Дръжте маркера за магазин самостоятелно в accept и поставете низовете за предизвикателства в fail.
Вашият собствен Cookie хедър променя поведението на повторните опити. Когато даден сайт отхвърли браузъра, който сме подали, Proxy Finder обикновено преминава към друга фамилия браузъри при следващия опит. Не и когато е прикачена вашата cookie. Дадена сесия е обвързана със сигнатурата, която я е спечелила, така че заявка, носеща собствена cookie, запазва браузъра, с който е започнала. Ако сайтът на дадена верига се окаже капризен към браузърите, изберете такъв изрично с browser profiles, вместо да разчитате на ротация.
А неуспешната задача ви показва точно какъв проблем имате. Всеки неуспешен Proxy Finder отговор носи attemptReport. defense брои отговорите, при които е разпозната проверка за ботове, noResponse брои изходите, които никога не са достигнали сайта, а contentRejected брои страниците, които са се върнали като HTTP 200 без проверка за ботове и са били отхвърлени единствено от вашето правило за съдържание. За инструмент за събиране на данни от супермаркети последното преброяване означава нещо конкретно: сайтът е отговорил, но не за вашия магазин. Повече изходи няма да решат това. Ръководството за отчети за опитите покрива останалите стойности.
Фиксирайте купувача или измерете отклонението
Констатациите от Instacart добавят втора променлива и има два честни начина за справяне с нея.
За ценови панел дръжте събирането на данни за всеки магазин към една самоличност. Изпълнете повторно изхода, който е доставил резултат (r["proxy"], непрозрачен ID), през Single със същата cookie и запазете този ID заедно с X-FourA-Request-Id хедъра на отговора към всеки ред. Когато дадена цена се промени рязко, можете да различите промяна в магазина от промяна на сесията.
За ценово проучване направете точно обратното нарочно. Вземете извадка за един и същ SKU в същия магазин през няколко независими сесии и запазете разпределението, а не първия отговор. Панел, който отчита едно число там, където купувачите виждат пет, не е прецизен. Той просто има късмет.
Резултати
Вземете регионална верига, която сравнява 120 магазина на конкуренти за 2500 SKU, обновявани ежедневно (илюстративен сценарий, базиран на индустриални стандарти). Това са 300 000 четения на страници на ден, като във всяка отделна нощ част от тях ще се върнат за грешен магазин: форматът на cookie се е променил, магазинът е затворил или сайтът е започнал да предпочита IP локацията пред съответната cookie.
- Страниците за грешен магазин се провалят още при събирането. Те никога не стават редове, така че промяната на цена в панела е промяна точно в този магазин.
- Грешките идват сортирани. Високи стойности на
contentRejectedотиват при отговорника за избор на магазин, високи стойности наdefenseса проблем с достъпа, високи стойности наnoResponseса exits. Трима отговорници, три решения, и никой не губи половин ден в гадаене кое от тях е. - Всеки ред е проследим. Exit ID и request ID за всяко наблюдение превръщат въпроса "този скок реален ли е?" в обикновена справка.
- Разсейването става конкретно число. За артикулите, които наблюдавате през различни сесии, можете да отчетете реалния диапазон, който купувачите виждат, вместо само едно отчитане от него.
Къде свършва това: цените за членове и програми за лоялност изискват профил, а експериментите, насочени към логнат потребител, изобщо няма да се появят в анонимни сесии. Събирането на тези данни е въпрос на общи условия, преди да бъде инженерен проблем. А маркерът за магазин е толкова добър, колкото е добър изборът му. Взимайте го от сорс кода на страницата, а не по памет, и го проверявайте отново при всеки редизайн на веригата.
Key Takeaway
Цената в супермаркета беше факт за даден продукт. Сега тя е факт за продукт, магазин и клиент, а collector, който записва само първото, генерира шум с много подредена схема.
Ако персонализираното ценообразуване продължи да се разпространява, въпросът на купувачите към данните за супермаркети се променя от "колко струва?" на "колко струва тук и колко широк е диапазонът?". Затова надеждните collectors няма да са тези с най-много exits. Ще бъдат тези, чийто всеки request може да покаже за кой магазин е бил и да го докаже.