أتمتة مراقبة أسعار المنافسين بدون Scraping عدواني
هتكسب نظام يومي يراجع أسعار 20 منتجًا في 3 دقائق، ويسجل الفرق في Google Sheets، بدل ما تفتح 20 صفحة يدويًا كل صباح.
مستوى القارئ: متوسط
المشكلة باختصار
لو عندك متجر صغير أو SaaS بيقارن باقات منافسة، المتابعة اليدوية بتفشل بسرعة. أول أسبوع بتكون ملتزم. بعد شهر، بتنسى يومين، وبعدها الأرقام تبقى إحساس مش بيانات.
الطريقة الشائعة الغلط هي إنك تشغل crawler سريع يلف على كل صفحات المنافس كل ساعة. الطريقة دي بتفشل في نقطتين: ممكن تضغط على السيرفر، وممكن تجمع بيانات أنت مش محتاجها أصلاً. الأفضل هنا أوتوميشن ضيق: صفحات قليلة، مرة واحدة يوميًا، واحترام واضح لـ robots.txt وشروط الموقع.
الفكرة الأساسية: اقرأ أقل وسجل المهم
الافتراض إن عندك 10 إلى 50 صفحة منتجات عامة، وكل صفحة فيها سعر ظاهر للزائر. مش بنتكلم عن تخطي login، ولا كسر rate limits، ولا نسخ كتالوج كامل. ركز: الهدف قرار تسعير، مش بناء نسخة من موقع المنافس.
Playwright مناسب هنا لأنه يفتح الصفحة مثل المتصفح، ويتعامل مع صفحات JavaScript أفضل من curl البسيط. حسب توثيق Playwright الرسمي، الأداة تدعم Chromium وFirefox وWebKit وتشتغل headless في بيئات CI أو السيرفرات. أما Google Sheets API فمفيد لتسجيل الصفوف؛ توثيق Google يوضح أن spreadsheets.values.append يضيف القيم في أول صف متاح داخل النطاق المحدد.
الـ trade-off هنا واضح. بتكسب دقة أعلى مع صفحات dynamic، لكن بتخسر موارد أكثر من HTTP request عادي. لو الصفحة static والسعر داخل HTML مباشر، استخدم fetch وcheerio بدل Playwright.
مثال تنفيذي: Playwright إلى Google Sheets
ابدأ بملف صغير. القائمة هنا مقصودة وصريحة. لا تعمل discovery تلقائي لكل روابط الموقع.
mkdir price-watch && cd price-watch
npm init -y
npm i playwright googleapis
npx playwright install chromiumثم اعمل ملف watch-prices.mjs. عدّل selectors حسب الصفحات التي تتابعها.
import { chromium } from 'playwright';
import { google } from 'googleapis';
const products = [
{ name: 'Pro Plan A', url: 'https://example.com/pricing', selector: '.pro-price' },
{ name: 'Starter Tool B', url: 'https://example.org/plans', selector: '[data-price="starter"]' }
];
function parsePrice(text) {
const match = text.replace(/,/g, '').match(/\d+(\.\d+)?/);
return match ? Number(match[0]) : null;
}
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ userAgent: 'PriceWatchBot/1.0 contact: you@example.com' });
const rows = [];
for (const item of products) {
await page.goto(item.url, { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(1500);
const text = await page.locator(item.selector).first().innerText({ timeout: 10000 });
const price = parsePrice(text);
rows.push([new Date().toISOString(), item.name, item.url, price, text.trim()]);
}
await browser.close();
const auth = new google.auth.GoogleAuth({
keyFile: 'service-account.json',
scopes: ['https://www.googleapis.com/auth/spreadsheets']
});
const sheets = google.sheets({ version: 'v4', auth });
await sheets.spreadsheets.values.append({
spreadsheetId: process.env.SHEET_ID,
range: 'prices!A:E',
valueInputOption: 'RAW',
requestBody: { values: rows }
});
console.log(`saved ${rows.length} price rows`);في سيناريو واقعي، لو عندك 30 منتجًا وكل صفحة تأخذ 4 ثوانٍ بين تحميل وانتظار خفيف، التشغيل كله هيأخذ حوالي دقيقتين. لو كنت بتراجع نفس الصفحات يدويًا، غالبًا هتدفع 20 إلى 30 دقيقة يوميًا. المكسب حوالي ساعتين أسبوعيًا، بشرط إن selectors ثابتة.
التشغيل اليومي بدون إزعاج
شغّل السكربت مرة يوميًا من cron على سيرفر صغير، أو من VPS موجود عندك. خلي التشغيل في وقت بعيد عن ساعات الذروة، مثل 03:20 صباحًا بتوقيت السوق المستهدف.
SHEET_ID="your_sheet_id" node watch-prices.mjs
# crontab: تشغيل يومي 03:20
20 3 * * * cd /opt/price-watch && SHEET_ID="your_sheet_id" /usr/bin/node watch-prices.mjs >> run.log 2>> error.logبدل ما تبعت تنبيه لكل قراءة، ابعت تنبيه فقط عند فرق مهم. مثال: لو السعر تغير أكثر من 5% عن آخر قراءة. كده تقلل noise، وتخلي التنبيه قرارًا وليس إشعارًا مزعجًا.
Trade-offs وما يجب الانتباه له
أفضل طريقة هنا ليست الأسرع. هي الأكثر احترامًا وحدودًا. استخدم تأخير 1 إلى 3 ثوانٍ بين الصفحات، ولا تعمل parallel كبير. بتكسب علاقة أقل عدوانية مع المواقع، وتخسر سرعة تنفيذ بسيطة. هذا مقبول لأن التشغيل يومي وليس realtime.
اقرأ robots.txt وشروط الاستخدام قبل أي تشغيل. Google Search Central يوضح أن robots.txt لإدارة وصول crawlers وتقليل الضغط، وليس وسيلة لإخفاء الصفحات من الفهرسة. المعنى العملي: وجود صفحة عامة لا يعني أن كل جمع آلي مناسب.
لو selector اتغير، السعر ممكن يرجع null. اعمل فشل واضح بدل ما تسجل صفر. الصفر هنا كارثة؛ لأنه ممكن يظهر كخصم 100%. استخدم validation: لو السعر null، سجّل الخطأ ولا تكتب رقمًا مضللًا.
متى لا تستخدم هذه الطريقة
لا تستخدمها لو البيانات خلف login، أو شروط الموقع تمنع الجمع الآلي، أو الصفحات كثيرة جدًا لدرجة تتحول إلى crawling كامل. لا تستخدمها أيضًا لو السعر يتغير كل دقيقة؛ هنا تحتاج API أو مزود بيانات رسمي. ولو عندك أقل من 5 منتجات تتراجع مرة أسبوعيًا، Sheet يدوي قد يكون أرخص من صيانة السكربت.
مصادر
- توثيق Playwright: التثبيت والتشغيل والمتصفحات المدعومة
- توثيق Playwright Locator: التعامل مع عناصر الصفحة
- Google Sheets API: append values
- Google Search Central: مقدمة robots.txt
الخطوة التالية
افتح قائمة المنتجات التي تراجعها يدويًا، واختر 5 صفحات فقط كبداية. اكتب selector لكل سعر، وشغّل السكربت يومين بدون تنبيهات. لو القراءات ثابتة وصحيحة، زوّد العدد تدريجيًا.