أتمتة تقارير الأداء الأسبوعية عبر إرسال بيانات السيو إلى Discord و Slack

دقائق القراءة: 11

أتمتة تقارير الأداء الأسبوعية عبر إرسال بيانات السيو إلى Discord و Slack

تُعد أتمتة تقارير الأداء الأسبوعية عبر إرسال بيانات السيو إلى Discord و Slack حلاً ثورياً للمسوقين الرقميين وأخصائيي تحسين محركات البحث (SEO) الذين يجدون أنفسهم غارقين في المهام الروتينية المتكررة. في عالم التسويق الرقمي سريع التطور، يُعد الوقت مورداً ثميناً، وغالباً ما تُهدر ساعات طويلة في تجميع البيانات يدوياً من أدوات مثل Google Search Console، وتنسيقها، ثم مشاركتها مع الفريق. هذه العملية لا تستنزف الوقت فحسب، بل تزيد أيضاً من احتمالية الأخطاء البشرية وتؤخر الحصول على رؤى قيمة. تخيل أن تتلقى تقريراً مفصلاً بأداء موقعك في محركات البحث كل صباح اثنين، مباشرة في قناتك المفضلة على Discord أو Slack، دون أي تدخل يدوي منك. هذا المقال سيأخذك في رحلة تفصيلية خطوة بخطوة لتحقيق هذا الهدف، باستخدام قوة لغة البرمجة Python وواجهات برمجة التطبيقات (APIs) المختلفة.

لماذا أتمتة تقارير السيو الأسبوعية؟

تتجاوز فوائد أتمتة تقارير السيو مجرد توفير الوقت، فهي تمثل نقلة نوعية في كيفية إدارة وتتبع أداء المواقع:

  • توفير الوقت والجهد: بدلاً من قضاء ساعات في استخراج البيانات وتنسيقها، يمكن للسكريبت أن يقوم بذلك في دقائق، مما يتيح لك التركيز على مهام أكثر استراتيجية.
  • الدقة والاتساق: تقلل الأتمتة من الأخطاء البشرية وتضمن أن تكون التقارير متسقة في شكلها ومحتواها أسبوعياً.
  • رؤى فورية وقابلة للتنفيذ: وصول سريع إلى البيانات يعني اتخاذ قرارات أسرع بناءً على أحدث المعلومات، مما يساعد في تعديل الاستراتيجيات بسرعة.
  • تحسين التعاون الجماعي: إرسال التقارير مباشرة إلى قنوات الاتصال الجماعية مثل Discord و Slack يضمن أن يكون جميع أعضاء الفريق على اطلاع دائم بأداء السيو، مما يعزز الشفافية والتعاون.
  • التركيز على التحليل: بدلاً من جمع البيانات، يصبح بإمكانك قضاء وقت أطول في تحليلها وفهم دلالاتها، مما يؤدي إلى تحسينات حقيقية في الأداء.

الأدوات والمكونات الأساسية

لتحقيق هذا المشروع، سنحتاج إلى مجموعة من الأدوات والتقنيات:

  • Python 3.x: لغة البرمجة التي سنستخدمها لكتابة السكريبت.
  • Google Search Console API: الواجهة التي تسمح لنا باستخراج بيانات السيو برمجياً.
  • Discord Webhooks: طريقة لإرسال رسائل آلية إلى قنوات Discord.
  • Slack Webhooks: طريقة لإرسال رسائل آلية إلى قنوات Slack.
  • المكتبات البرمجية في Python:
    • google-api-python-client و oauth2client للمصادقة والتفاعل مع Google APIs.
    • requests لإرسال طلبات HTTP POST إلى webhooks.
    • pandas لمعالجة وتنسيق البيانات.

الخطوات العملية لأتمتة التقارير

1. إعداد مشروع Google Cloud Platform و Search Console API

للوصول إلى بيانات Google Search Console برمجياً، ستحتاج إلى إعداد مشروع في Google Cloud Platform (GCP) وإنشاء حساب خدمة.

  1. إنشاء مشروع GCP: اذهب إلى Google Cloud Console وأنشئ مشروعاً جديداً.
  2. تفعيل Search Console API: من لوحة تحكم المشروع، ابحث عن “Google Search Console API” وقم بتفعيله.
  3. إنشاء حساب خدمة (Service Account):
    • من قائمة التنقل، اختر “IAM & Admin” ثم “Service Accounts“.
    • انقر على “CREATE SERVICE ACCOUNT“، أعطه اسماً ووصفاً.
    • في الخطوة الثانية، امنحه دور “Project > Viewer” أو دوراً أكثر تحديداً إذا كنت تفضل.
    • في الخطوة الثالثة، أنشئ مفتاحاً جديداً من نوع JSON وقم بتنزيله. احتفظ بهذا الملف بأمان، حيث يحتوي على بيانات اعتماد حساب الخدمة الخاص بك.
  4. منح حساب الخدمة حق الوصول إلى Google Search Console:
    • انسخ عنوان البريد الإلكتروني لحساب الخدمة (الذي ينتهي بـ @developer.gserviceaccount.com).
    • اذهب إلى Google Search Console الخاص بموقعك، اختر “Settings” ثم “Users and permissions“.
    • انقر على “Add user“، الصق عنوان البريد الإلكتروني لحساب الخدمة، وامنحه إذن “Full” (كامل) للوصول إلى بيانات الموقع.

2. إعداد Webhooks في Discord و Slack

تُعد Webhooks الطريقة الأسهل لإرسال رسائل آلية إلى قنوات Discord و Slack.

  1. لـ Discord:
    • افتح خادم Discord الخاص بك، اذهب إلى “Server Settings” ثم “Integrations“.
    • انقر على “Create Webhook“، ثم قم بتخصيص الاسم والقناة التي سيتم إرسال الرسائل إليها.
    • انسخ Webhook URL الذي يظهر لك.
  2. لـ Slack:
    • اذهب إلى Slack API وقم بإنشاء تطبيق جديد أو اختر تطبيقاً موجوداً.
    • من قائمة التطبيق، اختر “Incoming Webhooks” وقم بتفعيله.
    • انقر على “Add New Webhook to Workspace“، واختر القناة التي تريد إرسال الرسائل إليها.
    • انسخ Webhook URL الذي يظهر لك.
💡 ملاحظة فنية: احتفظ بـ Webhook URLs في مكان آمن، ولا تشاركها علناً، حيث يمكن لأي شخص لديه هذا الرابط إرسال رسائل إلى قناتك.

3. كتابة كود Python لاستخراج البيانات

الآن، لنبدأ بكتابة كود Python. تأكد من تثبيت المكتبات المطلوبة أولاً:


pip install google-api-python-client oauth2client requests pandas

هذا هو الجزء الأساسي من السكريبت الذي يستخرج البيانات من Google Search Console:


import os
from datetime import datetime, timedelta
import json

from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
import pandas as pd
import requests

# --- Configuration --- #
KEY_FILE_LOCATION = 'path/to/your/service_account_key.json' # Replace with your JSON key file path
SITE_URL = 'https://www.your-website.com/' # Replace with your website URL in GSC format

DISCORD_WEBHOOK_URL = 'YOUR_DISCORD_WEBHOOK_URL' # Replace with your Discord Webhook URL
SLACK_WEBHOOK_URL = 'YOUR_SLACK_WEBHOOK_URL'   # Replace with your Slack Webhook URL

# --- Google Search Console API Authentication --- #
def get_service_account_credentials():
    scope = ['https://www.googleapis.com/auth/webmasters.readonly']
    return ServiceAccountCredentials.from_json_keyfile_name(KEY_FILE_LOCATION, scope)

def get_search_console_service():
    credentials = get_service_account_credentials()
    return build('webmasters', 'v3', credentials=credentials)

# --- Data Extraction from GSC --- #
def get_search_console_data(service, site_url, start_date, end_date, dimensions=None, row_limit=50):
    if dimensions is None:
        dimensions = ['query'] # Default to query dimension

    request_body = {
        'startDate': start_date.strftime('%Y-%m-%d'),
        'endDate': end_date.strftime('%Y-%m-%d'),
        'dimensions': dimensions,
        'rowLimit': row_limit
    }

    try:
        response = service.searchanalytics().query(
            siteUrl=site_url,
            body=request_body
        ).execute()
        return response.get('rows', [])
    except Exception as e:
        print(f"Error fetching data from GSC: {e}")
        return []

# --- Main Script Logic --- #
if __name__ == '__main__':
    # Calculate dates for the last week (Sunday to Saturday)
    today = datetime.now()
    # Find the last Saturday (end of last week)
    last_saturday = today - timedelta(days=(today.weekday() + 2) % 7) # +2 for Sunday, +1 for Monday, etc.
    # Find the Sunday before last Saturday (start of last week)
    last_sunday = last_saturday - timedelta(days=6)

    start_date = last_sunday
    end_date = last_saturday

    print(f"Fetching data for the week: {start_date.strftime('%Y-%m-%d')} to {end_date.strftime('%Y-%m-%d')}")

    service = get_search_console_service()

    # Get top 10 queries
    queries_data = get_search_console_data(service, SITE_URL, start_date, end_date, dimensions=['query'], row_limit=10)
    queries_df = pd.DataFrame(queries_data)
    if not queries_df.empty:
        queries_df.columns = ['Query', 'Clicks', 'Impressions', 'CTR', 'Position']
        queries_df['CTR'] = queries_df['CTR'].apply(lambda x: f"{x:.2%}")
        queries_df['Position'] = queries_df['Position'].apply(lambda x: f"{x:.2f}")

    # Get top 10 pages
    pages_data = get_search_console_data(service, SITE_URL, start_date, end_date, dimensions=['page'], row_limit=10)
    pages_df = pd.DataFrame(pages_data)
    if not pages_df.empty:
        pages_df.columns = ['Page', 'Clicks', 'Impressions', 'CTR', 'Position']
        pages_df['CTR'] = pages_df['CTR'].apply(lambda x: f"{x:.2%}")
        pages_df['Position'] = pages_df['Position'].apply(lambda x: f"{x:.2f}")

    # Get overall site data (no dimensions)
    overall_data = get_search_console_data(service, SITE_URL, start_date, end_date, dimensions=[], row_limit=1)
    overall_df = pd.DataFrame(overall_data)
    if not overall_df.empty:
        overall_df.columns = ['Clicks', 'Impressions', 'CTR', 'Position']
        overall_df['CTR'] = overall_df['CTR'].apply(lambda x: f"{x:.2%}")
        overall_df['Position'] = overall_df['Position'].apply(lambda x: f"{x:.2f}")

    # ... (rest of the code for sending to Discord/Slack will go here)
💡 ملاحظة فنية: تأكد من استبدال 'path/to/your/service_account_key.json' بمسار ملف مفتاح JSON الذي قمت بتنزيله، و 'https://www.your-website.com/' بعنوان URL لموقعك كما هو مسجل في Google Search Console.

4. تنسيق البيانات وإرسالها إلى Discord

لإرسال البيانات إلى Discord، سنستخدم ميزة الـ embeds التي تسمح بتنسيق الرسائل بشكل جذاب.


# --- Discord Integration --- #
def send_to_discord(webhook_url, content, embeds=None):
    headers = {'Content-Type': 'application/json'}
    payload = {'content': content}
    if embeds:
        payload['embeds'] = embeds

    try:
        response = requests.post(webhook_url, data=json.dumps(payload), headers=headers)
        response.raise_for_status() # Raise an exception for HTTP errors
        print("Report sent to Discord successfully!")
    except requests.exceptions.RequestException as e:
        print(f"Error sending report to Discord: {e}")

# ... (inside if __name__ == '__main__': block, after data extraction)
    discord_embeds = []

    # Overall Data Embed
    if not overall_df.empty:
        overall_data_row = overall_df.iloc[0]
        discord_embeds.append({
            "title": f"تقرير أداء السيو الأسبوعي ({start_date.strftime('%Y-%m-%d')} - {end_date.strftime('%Y-%m-%d')})",
            "description": f"ملخص الأداء العام للموقع {SITE_URL}",
            "color": 3447003, # Blue color
            "fields": [
                {"name": "النقرات", "value": str(overall_data_row['Clicks']), "inline": True},
                {"name": "مرات الظهور", "value": str(overall_data_row['Impressions']), "inline": True},
                {"name": "متوسط نسبة النقر (CTR)", "value": str(overall_data_row['CTR']), "inline": True},
                {"name": "متوسط الموضع", "value": str(overall_data_row['Position']), "inline": True}
            ]
        })

    # Top Queries Embed
    if not queries_df.empty:
        query_fields = []
        for index, row in queries_df.iterrows():
            query_fields.append({
                "name": f"{index+1}. {row['Query']}",
                "value": f"نقرات: {row['Clicks']}, ظهور: {row['Impressions']}, CTR: {row['CTR']}, موضع: {row['Position']}",
                "inline": False
            })
        discord_embeds.append({
            "title": "أهم 10 استعلامات بحث",
            "description": "الاستعلامات التي جلبت أكبر عدد من النقرات",
            "color": 15844367, # Yellow color
            "fields": query_fields
        })

    # Top Pages Embed
    if not pages_df.empty:
        page_fields = []
        for index, row in pages_df.iterrows():
            page_fields.append({
                "name": f"{index+1}. {row['Page'].split('//')[1].split('/')[0] + '/...' if len(row['Page']) > 50 else row['Page']}", # Shorten URL for display
                "value": f"نقرات: {row['Clicks']}, ظهور: {row['Impressions']}, CTR: {row['CTR']}, موضع: {row['Position']}",
                "inline": False
            })
        discord_embeds.append({
            "title": "أهم 10 صفحات أداءً",
            "description": "الصفحات التي جلبت أكبر عدد من النقرات",
            "color": 5763719, # Green color
            "fields": page_fields
        })

    send_to_discord(DISCORD_WEBHOOK_URL, "", embeds=discord_embeds)

5. تنسيق البيانات وإرسالها إلى Slack

بالنسبة لـ Slack، سنستخدم blocks و attachments لتنسيق الرسالة.


# --- Slack Integration --- #
def send_to_slack(webhook_url, blocks=None, attachments=None):
    headers = {'Content-Type': 'application/json'}
    payload = {}
    if blocks:
        payload['blocks'] = blocks
    if attachments:
        payload['attachments'] = attachments

    try:
        response = requests.post(webhook_url, data=json.dumps(payload), headers=headers)
        response.raise_for_status() # Raise an exception for HTTP errors
        print("Report sent to Slack successfully!")
    except requests.exceptions.RequestException as e:
        print(f"Error sending report to Slack: {e}")

# ... (inside if __name__ == '__main__': block, after Discord sending logic)
    slack_blocks = []
    slack_attachments = []

    # Overall Data Block
    if not overall_df.empty:
        overall_data_row = overall_df.iloc[0]
        slack_blocks.append({
            "type": "header",
            "text": {"type": "plain_text", "text": f"تقرير أداء السيو الأسبوعي ({start_date.strftime('%Y-%m-%d')} - {end_date.strftime('%Y-%m-%d')})"}
        })
        slack_blocks.append({
            "type": "section",
            "text": {"type": "mrkdwn", "text": f"*ملخص الأداء العام للموقع {SITE_URL}*"}
        })
        slack_blocks.append({
            "type": "section",
            "fields": [
                {"type": "mrkdwn", "text": f"*النقرات:*\n{overall_data_row['Clicks']}"},
                {"type": "mrkdwn", "text": f"*مرات الظهور:*\n{overall_data_row['Impressions']}"},
                {"type": "mrkdwn", "text": f"*متوسط نسبة النقر (CTR):*\n{overall_data_row['CTR']}"},
                {"type": "mrkdwn", "text": f"*متوسط الموضع:*\n{overall_data_row['Position']}"}
            ]
        })
        slack_blocks.append({"type": "divider"})

    # Top Queries Attachment
    if not queries_df.empty:
        query_text = ""
        for index, row in queries_df.iterrows():
            query_text += f"{index+1}. *{row['Query']}*\nنقرات: {row['Clicks']}, ظهور: {row['Impressions']}, CTR: {row['CTR']}, موضع: {row['Position']}\n"
        slack_attachments.append({
            "color": "#F9A825", # Yellow color
            "blocks": [
                {"type": "section", "text": {"type": "mrkdwn", "text": "*أهم 10 استعلامات بحث*"}},
                {"type": "section", "text": {"type": "mrkdwn", "text": query_text}}
            ]
        })

    # Top Pages Attachment
    if not pages_df.empty:
        page_text = ""
        for index, row in pages_df.iterrows():
            page_text += f"{index+1}. *{row['Page'].split('//')[1].split('/')[0] + '/...' if len(row['Page']) > 50 else row['Page']}*\nنقرات: {row['Clicks']}, ظهور: {row['Impressions']}, CTR: {row['CTR']}, موضع: {row['Position']}\n"
        slack_attachments.append({
            "color": "#36A64F", # Green color
            "blocks": [
                {"type": "section", "text": {"type": "mrkdwn", "text": "*أهم 10 صفحات أداءً*"}},
                {"type": "section", "text": {"type": "mrkdwn", "text": page_text}}
            ]
        })

    send_to_slack(SLACK_WEBHOOK_URL, blocks=slack_blocks, attachments=slack_attachments)
💡 ملاحظة فنية: يمكن تخصيص الألوان والتنسيقات في Discord embeds و Slack blocks/attachments لتناسب هوية علامتك التجارية أو تفضيلات فريقك.

6. جدولة السكريبت (Cron Job أو Task Scheduler)

لجعل هذه العملية مؤتمتة بالكامل، تحتاج إلى جدولة تشغيل السكريبت بانتظام. الطريقة الأكثر شيوعاً هي استخدام cron job على أنظمة Linux و macOS، أو Task Scheduler على Windows.

  • على Linux/macOS (باستخدام cron):

    افتح محرر cron باستخدام الأمر crontab -e وأضف السطر التالي لتشغيل السكريبت كل يوم أحد في الساعة 9 صباحاً (على سبيل المثال):

    
    0 9 * * 0 /usr/bin/python3 /path/to/your/script.py
    

    تأكد من استبدال /usr/bin/python3 بالمسار الصحيح لمفسر Python الخاص بك و /path/to/your/script.py بالمسار الكامل لملف السكريبت.

  • على Windows (باستخدام Task Scheduler):

    ابحث عن “Task Scheduler” في قائمة ابدأ. أنشئ مهمة جديدة، وحدد وقت التشغيل (مثلاً، كل يوم أحد صباحاً)، ثم حدد الإجراء لتشغيل ملف Python الخاص بك باستخدام مفسر Python.

تحسينات وميزات إضافية

يمكن توسيع هذا السكريبت ليشمل ميزات أكثر تقدماً:

  • معالجة الأخطاء المحسّنة: إضافة المزيد من كتل try-except للتعامل مع الأخطاء المحتملة في الشبكة أو API.
  • نطاقات التاريخ الديناميكية: يمكن تعديل السكريبت ليسمح بتحديد نطاقات تاريخ مخصصة، أو مقارنة الأداء بالأسبوع السابق.
  • أبعاد إضافية: استخراج بيانات لأبعاد أخرى مثل الأجهزة، البلدان، أو أنواع البحث (صور، فيديو، أخبار).
  • تحليل البيانات المتقدم: دمج مكتبات مثل Matplotlib أو Seaborn لإنشاء رسوم بيانية بسيطة للاتجاهات وإرسالها كصور مع التقرير.
  • تخزين البيانات: حفظ البيانات المستخرجة في قاعدة بيانات أو ملف CSV للمراجعة المستقبلية أو التحليل التاريخي.

الخلاصة

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

الأسئلة الشائعة (FAQ)

س1: هل أحتاج إلى خبرة برمجية عميقة لتطبيق هذا؟
ج1: لا تحتاج إلى أن تكون مطور برمجيات محترفاً، ولكن معرفة أساسية بـ Python وكيفية التعامل مع سطر الأوامر ستكون مفيدة جداً. الكود المقدم هنا مصمم ليكون واضحاً ومباشراً، ويمكنك تعديله بناءً على احتياجاتك.
س2: ما هي تكلفة استخدام Google Search Console API؟
ج2: استخدام Google Search Console API مجاني تماماً. ومع ذلك، قد تكون هناك تكاليف مرتبطة بخدمات Google Cloud Platform الأخرى إذا قررت دمجها، لكن للوصول إلى بيانات Search Console، لا توجد رسوم مباشرة.
س3: هل يمكنني تخصيص شكل التقرير؟
ج3: نعم، الكود يوفر مرونة كبيرة لتخصيص شكل التقرير. يمكنك تعديل محتوى الـ embeds في Discord أو الـ blocks و attachments في Slack لإضافة المزيد من البيانات، تغيير الألوان، أو إعادة ترتيب المعلومات لتناسب تفضيلات فريقك.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *