بناء نظام لمراقبة أسعار المنتجات وإرسال بريد إلكتروني عند انخفاضها.
بناء نظام لمراقبة أسعار المنتجات وإرسال بريد إلكتروني عند انخفاضها
يُعد نظام مراقبة الأسعار من أكثر تطبيقات الأتمتة العملية فائدة في التجارة الإلكترونية، لأنه يحول متابعة المنتجات من عملية يدوية مرهقة إلى تدفق آلي دقيق يعتمد على جمع البيانات، تحليلها، ثم إرسال تنبيه فوري عند تحقق شرط محدد. هذا النوع من الأنظمة مناسب للمستهلك الذي ينتظر خصماً، ولمتجر يريد مراقبة المنافسين، ولمسؤول مشتريات يتابع أسعار الأدوات أو الاشتراكات.
فكرة النظام بسيطة ظاهرياً: نقرأ السعر الحالي لمنتج معين، نقارنه بالسعر المرجعي المخزن، ثم نرسل رسالة إذا انخفض السعر. لكن التنفيذ الاحترافي يتطلب فهماً عميقاً لمصادر البيانات، وجدولة التشغيل، حماية المفاتيح السرية، إدارة الأخطاء، ومنع التكرار في التنبيهات. وإذا كنت تحتاج أساساً مفاهيمياً قبل البدء، فمقال لماذا نحتاج الأتمتة؟ كيف توفر الشركات آلاف الساعات يوضح القيمة التشغيلية لمثل هذه الأنظمة.
البنية المعمارية للنظام
أفضل تصميم لهذا المشروع هو تقسيمه إلى أربع طبقات واضحة: طبقة جلب البيانات، طبقة المعالجة، طبقة التخزين، وطبقة التنبيه. هذا الفصل يجعل النظام قابلاً للتوسعة لاحقاً سواء أضفت عشرات المنتجات أو أكثر من قناة إشعار مثل البريد وTelegram.
- طبقة الجلب: قراءة السعر من
APIرسمي أو من الصفحة مباشرة. - طبقة المعالجة: تنظيف السعر وتحويله إلى رقم صالح للمقارنة.
- طبقة التخزين: حفظ آخر سعر، السعر المستهدف، وتاريخ آخر إشعار.
- طبقة التنبيه: إرسال بريد إلكتروني عند تحقق شرط الانخفاض.
الاعتماد على REST API إن وُجد هو الخيار الأفضل من حيث الاستقرار والوضوح. وإذا لم تتوفر واجهة رسمية، يمكن اللجوء إلى استخراج السعر من الصفحة عبر أتمتة المتصفح أو تحليل HTML. وللمفاهيم الأساسية المرتبطة بهذا القرار، راجع ما هو الـ API؟ شرح المفهوم بعيداً عن التعقيد التقني والفرق بين REST و SOAP و GraphQL: متى نختار كل نوع؟.
اختيار مصدر السعر: API أم Web Scraping؟
إذا كان المتجر أو المنصة يوفر Endpoint رسمياً للمنتجات، فهذه أفضل حالة. ستحصل عادة على السعر، العملة، حالة المخزون، وربما التخفيضات الحالية ضمن استجابة JSON سهلة المعالجة. أما إذا لم يتوفر ذلك، فالحل البديل يكون باستخدام Puppeteer أو Playwright لتجاوز الصفحات الديناميكية.
توصية هندسية: ابدأ دائماً بمحاولة الوصول إلى
APIرسمي أو غير معلن عبر أدوات المتصفح قبل استخدامWeb Scraping. فالواجهات أكثر استقراراً وأقل عرضة للكسر عند تغير تصميم الصفحة.
لفهم تركيب الطلبات بشكل أدق، يفيدك مقال تشريح طلب الـ API: الـ Endpoint، الـ Headers، والـ Body، كما أن مقال شرح أفعال الـ HTTP (GET, POST, PUT, DELETE) والفرق بينها يساعدك على فهم نوع الطلب المناسب في كل مرحلة.
تدفق البيانات داخل النظام
التدفق الجيد للبيانات لا يقل أهمية عن الكود نفسه. عند كل دورة تشغيل، يجب أن يمر النظام بالخطوات نفسها حتى تكون النتائج قابلة للتتبع والتدقيق. هذه الخطوات تمنع التنبيهات الخاطئة وتسهّل اكتشاف أسباب الفشل.
- تحميل قائمة المنتجات المراد تتبعها من ملف أو قاعدة بيانات.
- تنفيذ طلب
GETلكل منتج. - استخراج السعر الحالي وتحويله إلى قيمة رقمية موحدة.
- مقارنة السعر الحالي مع آخر سعر محفوظ أو مع السعر المستهدف.
- توليد حدث تنبيه إذا تحقق شرط الانخفاض.
- إرسال البريد الإلكتروني وتحديث سجل الإشعارات.
يفضَّل أن تُخزن البيانات الأولية ونتائج المقارنة في سجل تشغيلي log حتى تستطيع لاحقاً تحليل الأداء واكتشاف المشكلات. وإذا كنت تستخدم ردود JSON بكثافة، فمقال لغة الـ JSON: كيف تقرأ وتكتب البيانات التي تفهمها الآلات سيكون مرجعاً مهماً.
مثال عملي باستخدام JavaScript
في النموذج التالي سنفترض وجود مصدر أسعار عبر API، مع استخدام مكتبة إرسال بريد مثل nodemailer. المثال يوضح الهيكل العام ويمكن وصله بقاعدة بيانات لاحقاً.
import fetch from "node-fetch";
import nodemailer from "nodemailer";
import dotenv from "dotenv";
dotenv.config();
const products = [
{
id: "sku-1001",
name: "Wireless Headphones",
apiUrl: "https://api.example.com/products/sku-1001",
targetPrice: 79.99,
lastPrice: 99.99
}
];
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT),
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
});
async function fetchProductPrice(product) {
const response = await fetch(product.apiUrl, {
method: "GET",
headers: {
"Accept": "application/json",
"Authorization": `Bearer ${process.env.API_TOKEN}`
}
});
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}
const data = await response.json();
return {
currentPrice: Number(data.price),
currency: data.currency || "USD"
};
}
async function sendPriceAlert(product, currentPrice, currency) {
const subject = `Price Drop Alert: ${product.name}`;
const text = `${product.name} dropped to ${currentPrice} ${currency}`;
await transporter.sendMail({
from: process.env.MAIL_FROM,
to: process.env.MAIL_TO,
subject,
text
});
}
async function monitorPrices() {
for (const product of products) {
try {
const { currentPrice, currency } = await fetchProductPrice(product);
const isDrop = currentPrice < product.lastPrice;
const reachedTarget = currentPrice <= product.targetPrice;
if (isDrop || reachedTarget) {
await sendPriceAlert(product, currentPrice, currency);
product.lastPrice = currentPrice;
console.log(`Alert sent for ${product.name}`);
} else {
console.log(`No alert for ${product.name}`);
}
} catch (error) {
console.error(`Error monitoring ${product.name}:`, error.message);
}
}
}
monitorPrices();
توثيق نقطة الاتصال ومعالجة الاستجابة
مثال توثيق
Endpoint:
المسار:GET /products/{id}
التوثيق:Bearer Token
رؤوس الطلب:Accept: application/json
الاستجابة المتوقعة: السعر الحالي، العملة، رابط المنتج، وتاريخ آخر تحديث.
{
"id": "sku-1001",
"name": "Wireless Headphones",
"price": 74.99,
"currency": "USD",
"inStock": true,
"updatedAt": "2025-01-10T08:30:00Z"
}
أثناء القراءة من الواجهة، لا تفترض أن الحقول ستصل دائماً بالشكل نفسه. قد يعود السعر كنص، أو قد يتغير اسم الحقل من price إلى sale_price. لذلك، اجعل طبقة التحويل مرنة وواضحة. كما يفيد الرجوع إلى توثيق الـ API: كيفية قراءة مستندات Swagger و Redoc عند التعامل مع واجهات أكبر وأكثر تعقيداً.
إرسال البريد الإلكتروني بشكل موثوق
إرسال التنبيه ليس مجرد خطوة نهائية، بل نقطة حساسة تؤثر على موثوقية النظام بالكامل. يجب أن تحتوي الرسالة على اسم المنتج، السعر الجديد، السعر السابق، ورابط الشراء. هذا يجعل البريد قابلاً للتنفيذ الفوري بدلاً من كونه إشعاراً غامضاً.
من الأفضل أيضاً منع الإشعارات المتكررة لنفس السعر. فإذا انخفض المنتج إلى قيمة معينة وتم إرسال تنبيه، لا تكرر الإرسال في كل دورة تشغيل إلا إذا حدث انخفاض جديد أو مرّت مدة زمنية محددة. هذا النوع من deduplication يحسن تجربة المستخدم ويمنع تصنيف رسائلك كبريد مزعج.
الجدولة، الأمان، والاستقرار التشغيلي
لكي يعمل النظام تلقائياً، يجب تشغيله على فترات ثابتة باستخدام CRON Job أو من خلال بيئات أتمتة مثل Pipedream وMake. إذا أردت فهماً أوسع، راجع الجدولة الزمنية (CRON Jobs): كيف تجعل السكربت يعمل وأنت نائم واستخدام Pipedream للمبرمجين: دمج Node.js مع الأتمتة.
على جانب الأمان، لا تضع المفاتيح السرية أو بيانات SMTP داخل الكود مباشرة. استخدم ملف .env وخدمات إدارة الأسرار. وهنا يظهر دور مقال أمن البيانات: كيفية تخزين المفاتيح السرية في ملفات .env.، إضافة إلى مفاتيح الوصول (API Keys): كيف تحمي بابك الخلفي.
أخطاء حرجة يجب التعامل معها:
- وصول استجابة404لأن المنتج حُذف أو تغير رابطه.
- ظهور429بسبب تجاوزRate Limit.
- فشل التوثيق مع401أو403عند انتهاء الرمز أو نقص الصلاحيات.
- تغيّر بنية الصفحة أو الاستجابة، ما يؤدي إلى قراءة سعر خاطئ أو فارغ.
لفهم تلك الحالات، يفيدك الربط مع رموز الحالة (HTTP Status Codes): ماذا يخبرك السيرفر بـ 200 أو 404؟ وتحديد معدل الطلبات (Rate Limiting): كيف تتجنب الحظر من الخوادم.
متى نستخدم الأدوات بدون كود؟
ليس من الضروري دائماً بناء الحل بالكامل برمجياً. إذا كانت مصادر البيانات واضحة وعدد المنتجات محدوداً، يمكن تنفيذ سيناريو أولي عبر مقدمة في منصة Make (Integromat سابقاً): بناء سيناريوهات معقدة أو قوة Zapier: ربط أكثر من 5000 تطبيق بضغطات زر. لكن عند الحاجة إلى منطق مقارنة مخصص أو استخراج أسعار من صفحات معقدة، يصبح الحل البرمجي أكثر مرونة واستدامة. وللمقارنة المنهجية، راجع الأتمتة بدون كود (No-Code) مقابل الأتمتة البرمجية.
الخلاصة
بناء نظام لمراقبة أسعار المنتجات وإرسال بريد إلكتروني عند انخفاضها ليس مجرد سكربت بسيط يقرأ رقماً من صفحة، بل مشروع أتمتة متكامل يجمع بين جمع البيانات، التحقق من صحتها، المقارنة الذكية، الإشعار الموثوق، والحماية الأمنية. عندما تصمم هذا النظام بعقلية هندسية، ستتمكن من توسيعه لاحقاً ليشمل عدة متاجر، قواعد تنبيه مختلفة، أو قنوات إخطار إضافية.
النجاح هنا يعتمد على اختيار مصدر بيانات مستقر، جدولة تشغيل دقيقة، تخزين سليم للحالة السابقة، ومعالجة واضحة للأخطاء. بهذه المنهجية يتحول النظام من أداة تجريبية إلى خدمة تشغيلية يمكن الاعتماد عليها في المتابعة اليومية واتخاذ القرار السريع.