المقدمة: وش بنسوي اليوم؟
يا هلا والله! اليوم بنسوي نظام كشخة، بيساعدك تراقب أسعار المنتجات اللي في بالك على الإنترنت، وإذا نزل سعرها عن اللي أنت تبيه، بيجيك إيميل يبلغك. نظام بسيط لكنه بطل ويوفر عليك كثير.
العدة اللي نحتاجها
عشان نبني النظام هذا، بنستخدم بايثون (Python) وبعض المكتبات حقته:
- Python 3: هو أساس شغلنا.
- مكتبة
requests: عشان نقدر نرسل طلبات لصفحات الويب ونجيب محتواها. - مكتبة
BeautifulSoup4: هذي هي سكين الجزار اللي بنستخدمها عشان "نقطع" محتوى صفحة الويب ونطلع منه السعر. - مكتبة
smtplib: هذي مدمجة بـ Python، وبنستخدمها عشان نرسل الإيميلات. - ملف بسيط لتخزين البيانات (JSON): عشان نحفظ فيه المنتجات اللي نبي نراقبها.
الخطوة الأولى: تجهيز البيئة
أول شيء، لازم نجهز بيئة العمل حقتنا. دايماً أفضل إننا نستخدم بيئة افتراضية (Virtual Environment) عشان تكون مكتبات المشروع منعزلة وما تخرب على مشاريع ثانية. افتح الطرفية (Terminal أو Command Prompt) وسوي الآتي:
python -m venv price_tracker_env
# لتفعيل البيئة الافتراضية في macOS/Linux:
source price_tracker_env/bin/activate
# لتفعيل البيئة الافتراضية في Windows:
price_tracker_env\Scripts\activate
# بعدين، ثبت المكتبات اللي نحتاجها:
pip install requests beautifulsoup4
الخطوة الثانية: جلب السعر من صفحة المنتج (Web Scraping)
هذي أهم خطوة! هنا بنتعلم كيف ندخل على صفحة المنتج، ونسحب السعر منها. لازم تعرف إن كل موقع له طريقة عرض مختلفة للأسعار في كود الـ HTML، فالمثال اللي بنحطه هنا ممكن يحتاج تعديل بسيط حسب الموقع اللي بتراقبه.
ملاحظة: الـ web scraping يختلف من موقع لآخر. لازم تفحص الـ HTML حق الموقع (باستخدام F12 في المتصفح) وتشوف كيف السعر معروض (عادة يكون داخل
divأوspanولهclassأوidمميز). بعض المواقع قد تمنع الـ scraping أو يكون لها حماية، فكن حذر واستخدمها بمسؤولية.
سوي ملف جديد اسمه scraper.py وحط فيه الكود هذا:
import requests
from bs4 import BeautifulSoup
import re
def get_product_price(url):
# عشان المواقع ما تحظرنا، نحط User-Agent وكأننا متصفح حقيقي.
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # يرفع استثناء لو كان فيه خطأ في طلب HTTP (مثل 404 أو 500)
soup = BeautifulSoup(response.text, 'html.parser')
# هذا مجرد مثال على كيفية البحث عن السعر.
# لازم تعدله بناءً على الموقع اللي بتراقبه.
# غالبًا السعر يكون داخل عنصر له كلاس أو آيدي معين.
# مثال: نبحث عن عنصر يحتوي على كلمة "price" أو "السعر" في الكلاس الخاص به.
price_element = soup.find(class_=re.compile(r'price|السعر', re.IGNORECASE))
# لو ما لقينا عنصر مباشر، ممكن نبحث عن أرقام كبيرة في الصفحة
if not price_element:
# هذا ممكن يكون دقيق أو لا، حسب تصميم الموقع
potential_prices = re.findall(r'\b\d{1,3}(?:,\d{3})*(?:\.\d{1,2})?\b', response.text)
# نحاول نفلتر الأرقام اللي ممكن تكون أسعار
potential_prices = [float(p.replace(',', '')) for p in potential_prices if float(p.replace(',', '')) > 10]
if potential_prices:
# ممكن نختار أكبر سعر أو حسب السياق
price_element = max(potential_prices) # هذا بيعطينا الرقم مباشرة وليس عنصر BeautifulSoup
return price_element
if price_element:
price_text = price_element.get_text(strip=True)
# استخراج الأرقام من النص (معالجة العملات والفواصل)
# هذا النمط يبحث عن رقم يبدأ بـ digit، ثم يتبعه أرقام أو فواصل أو نقاط
price_match = re.search(r'\d[\d,\.]*', price_text)
if price_match:
price_str = price_match.group(0).replace(',', '') # نشيل الفواصل حقت الآلاف
return float(price_str)
return None
except requests.exceptions.RequestException as e:
print(f"خطأ في جلب الصفحة {url}: {e}")
return None
except Exception as e:
print(f"خطأ في تحليل السعر من {url}: {e}")
return None
# مثال للاستخدام (لازم تغير الرابط عشان تجربه):
# price = get_product_price("https://www.amazon.sa/dp/B08X5X1234")
# if price:
# print(f"السعر الحالي: {price} ريال")
# else:
# print("تعذر جلب السعر")
الخطوة الثالثة: تخزين المنتجات المراد مراقبتها
عشان يكون نظامنا ذكي، نحتاج نحفظ المنتجات اللي بنراقبها مع السعر المستهدف لكل منتج. ملف JSON بسيط بيكون ممتاز لهالشيء. سوي ملف جديد اسمه products.json وحط فيه المنتجات اللي تبي تراقبها بالشكل هذا:
[
{
"name": "سماعات سوني WH-1000XM4",
"url": "https://www.amazon.sa/Sony-WH-1000XM4-Cancelling-Headphones/dp/B08G4J4QG5/",
"target_price": 800.0,
"currency": "SAR"
},
{
"name": "لابتوب ماك بوك برو M1",
"url": "https://www.amazon.sa/Apple-MacBook-Pro-Chip-Space/dp/B08N5N4K5K/",
"target_price": 4500.0,
"currency": "SAR"
}
]
السكربت بيضيف حقل جديد اسمه last_notified_price للمنتج بعد أول تنبيه، عشان ما يرسل لك إيميلات متكررة بنفس السعر.
الخطوة الرابعة: إرسال الإيميلات
الآن، نجي لجزء الإيميلات. بنستخدم مكتبة smtplib المدمجة في بايثون. عشان ترسل إيميل، بنحتاج إيميل مرسل (إيميلك الشخصي) وكلمة مرور خاصة بالتطبيق (App Password) إذا كنت تستخدم خدمات مثل Gmail.
ملاحظة مهمة: لو بتستخدم Gmail، لازم تفعل Two-Factor Authentication (التحقق بخطوتين) أول شيء، بعدين تنشئ App Password (كلمة مرور خاصة بالتطبيق) من إعدادات حسابك. كلمة مرور حسابك الأساسية ما راح تشتغل مع تطبيقات الطرف الثالث زي سكريبتنا هذا.
سوي ملف جديد اسمه email_sender.py وحط فيه الكود هذا:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import os
def send_email(receiver_email, product_name, current_price, target_price, product_url):
sender_email = os.environ.get("EMAIL_SENDER") # إيميلك اللي بترسل منه
sender_password = os.environ.get("EMAIL_PASSWORD") # App Password حق الإيميل المرسل
if not sender_email or not sender_password:
print("خطأ: لم يتم تحديد إيميل المرسل أو كلمة المرور في المتغيرات البيئية.")
return
message = MIMEMultipart("alternative")
message["Subject"] = f"انخفاض سعر {product_name}!"
message["From"] = sender_email
message["To"] = receiver_email
html = f"""\
<html>
<body>
<p>يا هلا!</p>
<p>أبشرك، سعر منتج <b>{product_name}</b> نزل!</p>
<p>السعر الحالي: <b>{current_price:.2f} ريال</b>.</p>
<p>السعر المستهدف اللي حطيته: <b>{target_price:.2f} ريال</b>.</p>
<p>تقدر تشوف المنتج من هنا: <a href=\"{product_url}\">{product_url}</a></p>
<p>بالتوفيق،</p>
<p>نظام مراقبة الأسعار الخاص بك.</p>
</body>
</html>
"""
part1 = MIMEText(html, "html")
message.attach(part1)
try:
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(sender_email, sender_password)
server.sendmail(sender_email, receiver_email, message.as_string())
print(f"تم إرسال إيميل بخصوص {product_name} إلى {receiver_email}")
except Exception as e:
print(f"خطأ في إرسال الإيميل: {e}")
# مثال للاستخدام (لازم تحط القيم الصحيحة بدل الأمثلة):
# send_email("your_email@example.com", "سماعات سوني", 750.0, 800.0, "https://www.amazon.sa/...")
عشان نستخدم EMAIL_SENDER و EMAIL_PASSWORD بأمان وما تكون ظاهرة مباشرة في الكود، الأفضل إنك تحطها كمتغيرات بيئية (Environment Variables) في نظام التشغيل حقك. هذا يحمي معلوماتك الحساسة.
مثال لتعيين متغيرات بيئية في Linux/macOS (في الطرفية قبل تشغيل السكريبت):
export EMAIL_SENDER="your_email@gmail.com"
export EMAIL_PASSWORD="your_app_password"
export RECEIVER_EMAIL="your_personal_email@example.com"
وفي Windows (من سطر الأوامر):
set EMAIL_SENDER="your_email@gmail.com"
set EMAIL_PASSWORD="your_app_password"
set RECEIVER_EMAIL="your_personal_email@example.com"
الخطوة الخامسة: تجميع كل شيء في سكريبت واحد
الآن، بنجمع كل الأجزاء اللي سويناها في سكريبت واحد هو اللي بيشغل النظام كله. سوي ملف جديد اسمه main_tracker.py وحط فيه الكود هذا:
import json
from scraper import get_product_price
from email_sender import send_email
import time
import os
# إيميلك اللي بتوصله التنبيهات (لازم تحدده كمتغير بيئي RECEIVER_EMAIL)
RECEIVER_EMAIL = os.environ.get("RECEIVER_EMAIL")
def load_products(filename="products.json"):
try:
with open(filename, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
print(f"الملف {filename} غير موجود. يرجى التأكد من وجوده.")
return []
except json.JSONDecodeError:
print(f"خطأ في قراءة ملف {filename}. تأكد أنه ملف JSON سليم.")
return []
def save_products(products, filename="products.json"):
with open(filename, 'w', encoding='utf-8') as f:
json.dump(products, f, indent=4, ensure_ascii=False)
def main():
if not RECEIVER_EMAIL:
print("خطأ: لم يتم تحديد إيميل المستلم (RECEIVER_EMAIL) في المتغيرات البيئية. يرجى تعيينه.")
return
products = load_products()
if not products:
print("لا توجد منتجات للمراقبة. يرجى إضافة منتجات إلى ملف products.json.")
return
for product in products:
product_name = product["name"]
product_url = product["url"]
target_price = product["target_price"]
last_notified_price = product.get("last_notified_price") # عشان ما نرسل إيميل كل شوي بنفس السعر
print(f"جاري مراقبة: {product_name} - الرابط: {product_url}")
current_price = get_product_price(product_url)
if current_price is not None:
print(f"السعر الحالي لـ {product_name}: {current_price:.2f} ريال")
if current_price <= target_price:
# نتحقق إذا السعر الحالي أقل أو يساوي السعر المستهدف
# ونتأكد إننا ما أرسلنا تنبيه بنفس السعر من قبل (أو لو نزل أكثر)
if last_notified_price is None or current_price < last_notified_price:
print(f"انخفاض سعر {product_name}! السعر الحالي {current_price:.2f} ريال، المستهدف {target_price:.2f} ريال.")
send_email(RECEIVER_EMAIL, product_name, current_price, target_price, product_url)
product["last_notified_price"] = current_price # تحديث آخر سعر تم الإبلاغ عنه
else:
print(f"السعر ما زال منخفضاً لكنه لم يتغير عن آخر تنبيه ({last_notified_price:.2f}).")
else:
print(f"السعر الحالي {current_price:.2f} ريال أعلى من المستهدف {target_price:.2f} ريال.")
# لو السعر ارتفع فوق المستهدف، نصفر آخر سعر تم الإبلاغ عنه عشان لو نزل مرة ثانية يبلغنا.
if product.get("last_notified_price") is not None:
product["last_notified_price"] = None
else:
print(f"لم نتمكن من جلب سعر {product_name}.")
time.sleep(5) # انتظار بسيط بين كل منتج عشان ما نحظر من الموقع
save_products(products) # حفظ أي تحديثات (مثل last_notified_price)
if __name__ == "__main__":
main()
الخطوة السادسة: جدولة تشغيل السكريبت
عشان يشتغل النظام هذا بشكل تلقائي ويراقب الأسعار باستمرار، لازم نجدول السكريبت حقنا عشان يشتغل كل فترة (مثلاً كل ساعة، أو كل 6 ساعات). الطريقة تختلف حسب نظام التشغيل:
في Linux/macOS (باستخدام Cron):
افتح الطرفية واكتب الأمر هذا عشان تعدل مهام Cron:
crontab -e
بعدين أضف هذا السطر في آخر الملف اللي بيفتح لك (تأكد من تعديل المسارات عشان تتناسب مع جهازك):
0 */6 * * * /usr/bin/python3 /path/to/your/project/main_tracker.py >> /path/to/your/project/tracker.log 2>&1
السطر اللي فوق يشغل السكريبت كل 6 ساعات (عند الدقيقة صفر من كل ساعة سادسة). تأكد إنك تحط المسار الكامل لـ
python3(تقدر تعرفه بكتابةwhich python3في الطرفية) والمسار الكامل لملفmain_tracker.py. والـ>> tracker.log 2>&1بيحفظ أي مخرجات أو أخطاء في ملفtracker.logعشان تقدر تراجعها لو صار شيء.
في Windows (باستخدام Task Scheduler):
ابحث عن "Task Scheduler" (جدولة المهام) في قائمة ابدأ في الويندوز وافتحه. بعدين اتبع الخطوات هذي:
- انقر على "Create Basic Task..." (إنشاء مهمة أساسية...).
- أعطِ المهمة اسم ووصف (مثلاً: "مراقبة أسعار المنتجات").
- اختر متى تبي تشغل المهمة (مثلاً: "Daily" يومياً، أو "Weekly" أسبوعياً).
- حدد الوقت والتكرار اللي يناسبك.
- في قسم Action (إجراء)، اختر "Start a program" (بدء برنامج).
- في خانة "Program/script": اكتب المسار الكامل لـ
python.exeفي بيئتك الافتراضية (مثلاً:C:\path\to\price_tracker_env\Scripts\python.exe). - في خانة "Add arguments (optional)": اكتب المسار الكامل لملف
main_tracker.py(مثلاً:C:\path\to\your\project\main_tracker.py). - انقر Finish (إنهاء).
ختاماً
كذا نكون بنينا نظام بسيط ومفيد لمراقبة أسعار المنتجات وإرسال تنبيهات بالإيميل عند انخفاضها. تقدر تطور عليه وتضيف مميزات أكثر، مثل دعم مواقع أكثر، أو حفظ سجل بالأسعار في قاعدة بيانات فعلية (بدل ملف JSON)، أو حتى إضافة واجهة رسومية بسيطة. الشغل هذا هو أساس لأشياء أكبر وأكثر تعقيداً. بالتوفيق في رحلتك البرمجية!