← كل المقالات

استخراج أسعار البقالة: أي متجر، وأي متسوق؟

يفشل استخراج أسعار البقالة بصمت: يؤدي فقدان cookie المتجر إلى إرجاع سعر حقيقي للمتجر الخطأ. كيفية إثبات المتجر، ولماذا لا يكفي متسوق واحد.

عُرضت دزينة من بيض 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 كيف أصبح تسعير التجزئة في أستراليا معتمدا على الرمز البريدي على مدار ما يقرب من 4 سنوات من عمل الشركة.

ثم يأتي دور المتسوق. في ديسمبر 2025، أجرت كل من Groundwork Collaborative و Consumer Reports و More Perfect Union تجارب حية شملت 437 متسوقا في 4 مدن، حيث قاموا بملء سلات تسوق متطابقة على Instacart من نفس المتاجر في نفس الوقت. ظهرت 74% من المنتجات بأكثر من سعر واحد. وعند اختلاف الأسعار، بلغ متوسط الفجوة بين أدنى سعر وأعلاه 13%، بينما تباينت السلات الكاملة بنحو 7%.

اجمع هذه العوامل معا وستحصل على الخلل الذي يجعل جمع بيانات البقالة مكلفا. إذا فقدت سياق المتجر، فلن يتعطل أي شيء. لن يرجع الموقع خطأ. بل سيرجع سعرا صالحا تماما لمتجر لم تطلبه، مع SKU ورقم وطابع زمني يجتازون كل فحص للقيم الفارغة (null check) لديك.

لقد كتبنا سابقا عن الحالة التي يبدو فيها الحظر كنقطة بيانات. لكن اكتشاف هذه الحالة أصعب، لأن الصفحة هي بالفعل صفحة أسعار حقيقية. فقط ليست الصفحة التي تقصدها.

إثبات هوية المتجر داخل الاستجابة

ترسل معظم أدوات الجمع محدد المتجر (سواء كان cookie أو query parameter أو header) وتفترض صحته. لكن الممارسة الأكثر متانة هي إلزام الاستجابة بإثبات ذلك. إذا كانت الصفحة تذكر اسم المتجر الذي جرى عرضها لأجله، مثل معرف متجر (store 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"]

ثلاث تفاصيل في هذا الـ request يسهل الخطأ فيها.

ينجح accept عندما تظهر أي من سلاسله النصية في الصفحة. إذا وضعت محدد السعر بجانب محدد المتجر، فستمر صفحة متجر خاطئ بنجاح، لأنها تحتوي على سعر أيضا. احتفظ بمحدد المتجر وحده في accept، وضع السلاسل النصية الخاصة بالتحديات في fail.

يغير الـ header الخاص بك Cookie سلوك عمليات إعادة المحاولة. عندما يرفض موقع المتصفح الذي قدمناه، ينتقل Proxy Finder عادة إلى عائلة متصفحات أخرى في المحاولة التالية. لا يحدث هذا عند إرفاق الـ cookie الخاص بك. ترتبط الجلسة بالبصمة التي أنشأتها، لذا فإن الـ request الذي يحمل الـ cookie الخاص به يحتفظ بالمتصفح الذي بدأ به. إذا تبين أن موقع سلسلة متاجر يدقق كثيرا في المتصفحات، فاختر واحدا بشكل صريح باستخدام browser profiles بدلا من الاعتماد على التدوير.

وتوضح لك المهمة الفاشلة نوع الفشل الذي واجهته. يحمل كل response فاشل من Proxy Finder الترويسة attemptReport. يحسب defense الردود التي تم فيها التعرف على فحص bot، ويحسب noResponse نقاط الخروج التي لم تصل إلى الموقع مطلقا، بينما يحسب contentRejected الصفحات التي عادت برمز HTTP 200 دون فحص bot ولكن تم استبعادها فقط بواسطة قاعدة المحتوى الخاصة بك. بالنسبة لمجمع بيانات البقالة، يعني هذا الحساب الأخير شيئا محددا: الموقع استجاب، لكن ليس للمتجر المطلوب. إضافة المزيد من نقاط الخروج لن يحل ذلك. يغطي دليل تقرير المحاولات الإحصاءات الأخرى.

تثبيت هوية المتسوق، أو قياس التباين

تضيف نتائج Instacart متغيرا ثانيا، وهناك طريقتان دقيقتان للتعامل معه.

بالنسبة للوحة مقارنة الأسعار، احتفظ بعملية جمع كل متجر تحت هوية واحدة. أعد تشغيل نقطة الخروج الناجحة (r["proxy"]، وهو معرف مبهم) عبر Single مع نفس الـ cookie، وخزن ذلك المعرف مع الـ response header رقم X-FourA-Request-Id في كل صف. عندما يقفز السعر، يمكنك التمييز بين تغير حدث في المتجر وتغير ناتج عن تبديل الجلسة.

بالنسبة لأبحاث التسعير، افعل العكس عن عمد. خذ عينات لنفس الـ SKU في نفس المتجر عبر عدة جلسات مستقلة واحتفظ بالتوزيع الإحصائي، وليس بالرد الأول فقط. لوحة البيانات التي تسجل رقما واحدا بينما يرى المتسوقون خمسة أرقام مختلفة ليست دقيقة، بل هي محظوظة فحسب.

النتائج

لنفترض وجود سلسلة إقليمية تقيس 120 متجرا منافسا على 2500 وحدة SKU، مع التحديث يوميا (سيناريو توضيحي يعتمد على معايير القطاع). يمثل ذلك 300,000 قراءة صفحة يوميا، وفي أي ليلة ستعود بعض هذه القراءات للمتجر الخطأ: تغير تنسيق الـ cookie، أو أغلق متجر، أو بدأ موقع في تفضيل الموقع الجغرافي للـ IP على الـ cookie.

  • فشل صفحات المتجر الخاطئ عند مرحلة الجمع. فهي لا تتحول أبدًا إلى صفوف، لذا فإن أي تغير في السعر ضمن اللوحة هو تغير فعلي في ذلك المتجر.
  • تصل حالات الفشل مصنفة. ارتفاع contentRejected يذهب إلى المسؤول عن تحديد المتجر، وارتفاع defense يشير إلى مشكلة في الوصول، وارتفاع noResponse يتعلق بنقاط الخروج (exits). ثلاثة مسؤولين، وثلاثة حلول، دون أن يضيع أحد وقته في التخمين.
  • كل صف قابل للتتبع. وجود معرف نقطة الخروج (exit ID) ومعرف الطلب (request ID) لكل عملية رصد يحول سؤال "هل هذا الارتفاع المفاجئ حقيقي؟" إلى مجرد عملية بحث سريعة.
  • فارق السعر يصبح رقمًا محددًا لديك. بالنسبة لوحدات SKU التي تجمع عينات منها عبر الجلسات، يمكنك تقديم النطاق الفعلي الذي يواجهه المتسوقون بدلاً من قراءة واحدة منه.

حدود هذا النهج: أسعار الأعضاء وبرامج الولاء تتطلب تسجيل الدخول، والتجارب المرتبطة بمتسوق مسجل لن تظهر إطلاقًا في الجلسات المجهولة. جمع هذه البيانات مسألة تتعلق بشروط الخدمة قبل أن تكون مسألة هندسية. كما أن دقة محدد المتجر تعتمد على طريقة اختيارك له؛ استخرجه من مصدر الصفحة مباشرة وليس من الذاكرة، وأعد التحقق منه كلما أعادت السلسلة تصميم موقعها.

Key Takeaway

كان سعر البقالة في السابق حقيقة مرتبطة بالمنتج فقط. أما الآن، فهو حقيقة ترتبط بالمنتج، والمتجر، والمتسوق معا، وأي أداة جمع تسجل العنصر الأول فقط تنتج بيانات غير دقيقة ذات هيكلية منظمة ظاهريا.

إذا استمر انتشار التسعير المخصص لكل متسوق، فإن السؤال الذي يطرحه مشترو بيانات البقالة سينتقل من "كم تبلغ تكلفته؟" إلى "كم تبلغ تكلفته هنا، وما مدى اتساع نطاق السعر؟". لذلك، فإن أدوات الجمع الموثوقة لن تكون تلك التي تمتلك أكبر عدد من نقاط الخروج، بل تلك التي يمكن لكل طلب فيها تحديد المتجر المستهدف وإثبات ذلك.