بناء أداة لمراقبة ترتيب الكلمات المفتاحية يومياً بدون أدوات مدفوعة


ماذا سنبني اليوم؟

سنقوم ببناء أداة بسيطة وفعالة باستخدام Google Sheets و Google Apps Script لمراقبة ترتيب كلماتك المفتاحية المستهدفة على محرك بحث Google بشكل يومي، وذلك بدون الحاجة إلى أي أدوات SEO مدفوعة.

المتطلبات

  • حساب Google (للوصول إلى Google Sheets و Google Apps Script).

الخطوة 1: إعداد جدول البيانات (Google Sheet)

قم بإنشاء جدول بيانات جديد في Google Sheets وقم بتسميته "مراقب ترتيب الكلمات المفتاحية".

في الورقة الأولى (Sheet1)، قم بإنشاء الأعمدة التالية:

  1. A: الكلمة المفتاحية (Keyword): الكلمة التي تريد مراقبة ترتيبها.
  2. B: النطاق المستهدف (Target Domain/URL): النطاق أو عنوان URL الخاص بموقعك (على سبيل المثال: example.com أو example.com/page-path).
  3. C: آخر ترتيب (Last Rank): سيتم تحديث هذا العمود بالترتيب الحالي.
  4. D: تاريخ آخر تحديث (Last Updated): تاريخ آخر مرة تم فيها فحص الترتيب.
ملاحظة تقنية: يجب أن يكون "النطاق المستهدف" هو النطاق الأساسي لموقعك (مثل example.com) أو عنوان URL لصفحة محددة (مثل example.com/your-page). ستستخدم الأداة هذا النص للبحث عنه ضمن الروابط في نتائج بحث Google.

الخطوة 2: كتابة كود Google Apps Script

من جدول البيانات الخاص بك، اذهب إلى الإضافات (Extensions) -> Apps Script. سيؤدي هذا إلى فتح محرر كود جديد.

قم بحذف أي كود موجود (عادةً ما يكون هناك myFunction() افتراضي) والصق الكود التالي:

// Get the active spreadsheet and the sheet where data is stored
const SPREADSHEET_NAME = "مراقب ترتيب الكلمات المفتاحية"; // تأكد من مطابقة هذا الاسم لاسم جدول البيانات الخاص بك
const SHEET_NAME = "Sheet1"; // تأكد من مطابقة هذا الاسم لاسم الورقة الخاصة بك

function updateKeywordRanks() {
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = spreadsheet.getSheetByName(SHEET_NAME);

  if (!sheet) {
    Logger.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">Error: Sheet '${SHEET_NAME}' not found.</code>);
    return;
  }

  // Get all data from the sheet, assuming headers are in row 1
  const dataRange = sheet.getDataRange();
  const values = dataRange.getValues();

  // Skip header row
  if (values.length < 2) {
    Logger.log("No data found in the sheet besides headers.");
    return;
  }

  // Iterate through each row (starting from the second row for data)
  for (let i = 1; i < values.length; i++) {
    const row = values[i];
    const keyword = row[0]; // Column A: Keyword
    const targetString = row[1]; // Column B: Target Domain or URL (e.g., example.com or example.com/page)

    if (!keyword || !targetString) {
      Logger.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">Skipping row ${i + 1} due to missing keyword or target string.</code>);
      continue;
    }

    Logger.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">Checking rank for keyword: "${keyword}" for target: "${targetString}"</code>);
    const rank = getRank(keyword, targetString);

    // Update the sheet with the new rank and current date
    // Columns C and D (index 2 and 3)
    sheet.getRange(i + 1, 3).setValue(rank); // Update Last Rank
    sheet.getRange(i + 1, 4).setValue(new Date()); // Update Last Updated Date
    
    // Add a small delay to avoid hitting rate limits too quickly
    Utilities.sleep(1000); // 1 second delay
  }
  Logger.log("Keyword rank update complete.");
}

/**
 * Fetches the Google search result page for a given keyword and tries to find the target string's rank.
 * WARNING: This method is highly fragile. Google frequently changes its HTML structure and implements
 * anti-scraping measures. This function is for demonstration purposes and might break often.
 * For robust, production-level scraping, consider using dedicated SEO APIs or proxy services.
 *
 * @param {string} keyword The keyword to search for.
 * @param {string} targetString The domain or URL string to look for (e.g., "example.com" or "example.com/page").
 * @returns {number} The 1-based rank if found, -1 if not found on the first page, -2 if an error occurred.
 */
function getRank(keyword, targetString) {
  // Use a specific Google TLD and language for more consistent (though not guaranteed) results
  // hl=en (host language), gl=us (geolocation) - feel free to change to your target region
  const searchUrl = <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">https://www.google.com/search?q=${encodeURIComponent(keyword)}&hl=en&gl=us</code>;

  try {
    // Fetch the search results page
    const response = UrlFetchApp.fetch(searchUrl, {muteHttpExceptions: true});
    const html = response.getContentText();

    // Regular expression to find <a href="/url?q=..." links. This pattern targets Google's
    // internal redirect links which typically wrap organic results.
    const linkRegex = /<a href="(\/url\?q=[^\"]+)"/g;
    let match;
    let rank = 0;
    const processedUrls = new Set(); // To store unique URLs already counted towards rank

    while ((match = linkRegex.exec(html)) !== null) {
      const fullGoogleRedirectLink = match[1];
      
      // Extract the actual URL from Google's redirect link
      const urlExtractorRegex = /\/url\?q=(.+?)&sa=/;
      const urlMatch = fullGoogleRedirectLink.match(urlExtractorRegex);

      if (urlMatch && urlMatch[1]) {
        const actualUrl = decodeURIComponent(urlMatch[1]);
        
        // Simple heuristic to filter out non-organic or irrelevant links (e.g., Google's own services, ads if present)
        // This is not foolproof and may require adjustment based on Google's ever-changing structure.
        if (actualUrl.startsWith("http") && 
            !actualUrl.includes("google.com/recaptcha") && 
            !actualUrl.includes("accounts.google.com") && 
            !actualUrl.includes("support.google.com") &&
            !actualUrl.includes("policies.google.com")) {
          
          // Increment rank only for unique, potentially organic result URLs
          // This avoids counting multiple links to the same page (e.g., main link, cached link, etc.) as separate ranks.
          if (!processedUrls.has(actualUrl)) {
            processedUrls.add(actualUrl);
            rank++;
            if (actualUrl.includes(targetString)) {
              return rank; // Found the target string!
            }
          }
        }
      }
      // Stop after checking a reasonable number of potential results to avoid infinite loops
      // and to focus on the first page results (e.g., first 100 links encountered).
      if (rank >= 100) break; 
    }
    return -1; // Target string not found within the checked results on the first page
  } catch (e) {
    Logger.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">Error in getRank for keyword "${keyword}": ${e.message}</code>);
    return -2; // Indicates an error during fetching or parsing
  }
}
ملاحظة تقنية هامة جداً: تعتمد دالة getRank على تحليل بنية HTML لصفحة نتائج بحث Google. هذه البنية تتغير بشكل متكرر، كما أن Google تطبق إجراءات صارمة لمكافحة الكشط (scraping)، مما قد يؤدي إلى حظر طلباتك أو عرض CAPTCHA أو تقديم نتائج غير دقيقة. هذه الطريقة مجانية ولكنها هشة للغاية وقد تتوقف عن العمل في أي وقت. للحصول على مراقبة ترتيب كلمات مفتاحية موثوقة ودقيقة على المدى الطويل، يوصى بشدة باستخدام واجهات برمجة تطبيقات (APIs) مخصصة لـ SEO أو أدوات مدفوعة.

الخطوة 3: جدولة التشغيل التلقائي

لجعل الأداة تعمل يومياً تلقائياً، يجب عليك إعداد مشغل (trigger) في Google Apps Script:

  1. في محرر Apps Script، على الشريط الجانبي الأيسر، انقر على أيقونة المنبه (Triggers).
  2. انقر على زر + إضافة مشغل (Add Trigger) في الزاوية اليمنى السفلية.
  3. في النافذة المنبثقة:
    • اختر الوظيفة المراد تشغيلها (Choose which function to run): اختر updateKeywordRanks.
    • اختر مصدر الحدث (Choose event source): اختر Time-driven.
    • اختر نوع المشغل الزمني (Select type of time-driven trigger): اختر Day timer.
    • اختر وقت اليوم (Select time of day): اختر الوقت الذي تفضل تشغيل السكريبت فيه (مثلاً، بين 1 صباحاً و 2 صباحاً).
  4. انقر على حفظ (Save). قد يطلب منك Google تفويض السكريبت للوصول إلى بياناتك (هذا طبيعي، لأنه يحتاج إلى قراءة وكتابة في Google Sheet والوصول إلى الإنترنت).

النتيجة النهائية المتوقعة

بعد إعداد جدول البيانات والكود والمشغل، ستقوم الأداة بتحديث ترتيب كلماتك المفتاحية يومياً تلقائياً. ستجد في عمود "آخر ترتيب" (Last Rank) الرقم الذي يمثل ترتيب موقعك على Google للكلمة المفتاحية المحددة (1 يعني المركز الأول، 10 يعني المركز العاشر، وهكذا). إذا لم يتم العثور على موقعك ضمن النتائج التي تم فحصها في الصفحة الأولى، فسيظهر الترتيب -1. وفي حال حدوث خطأ أثناء جلب البيانات، سيظهر الترتيب -2.

ستتمكن من تتبع أداء كلماتك المفتاحية بمرور الوقت مجاناً، وستكون لديك رؤية سريعة للتغيرات في ترتيب موقعك.