SEO تقني · 2026-09-16 · قراءة 14 دقيقة

تحليل ملفات السجل Log Files بدون أدوات باهظة الثمن

لا تحتاج منصة Enterprise للإجابة عن الأسئلة الأولى في تحليل السجلات: من طلب أي URL، متى، بأي status، وبأي حجم؟ ابدأ بتصدير محدود، وحّد الحقول، واحتفظ بالـraw log للتحقق.

النطاق: هذا مرجع تنفيذ عملي. راجع توثيق Google والمنصات الحالية قبل اتخاذ قرار إصدار أو سياسة.

مدخلات Nginx وCloudflare

سجلات Nginx تكون أسهل عندما تتحكم في format. أما Cloudflare فقد يستخدم schema مختلفًا، فوحّد الحقول أولًا. لا تحدد bot من User-Agent وحده في قرارات الأمان؛ اجمعه مع تحقق IP أو مزود الشبكة عند الحاجة.

# طلبات حسب User-Agent
awk -F'"' '{print $6}' access.log \
  | awk 'BEGIN{IGNORECASE=1} /Googlebot/{g++} /GPTBot|ClaudeBot|PerplexityBot/{ai++} !/bot|crawler|spider/{human++} END{print "google",g; print "ai",ai; print "human",human}'

# توزيع الحالات للزواحف
awk -F'"' '/Googlebot|GPTBot|ClaudeBot|PerplexityBot/ { split($1,a," "); print a[9] }' access.log | sort | uniq -c | sort -nr

Parser Python قابل للتكرار

حلل request line وUser-Agent بانتظام بدل split عشوائي. المثال التالي محافظ: السطر التالف يُعدّ ولا يُحذف بصمت.

import re
from collections import Counter
line_re = re.compile(r'^(?P<ts>\S+) \S+ "(?P<request>[^" ]+) (?P<status>\d{3}) (?P<bytes>\d+)" "(?P<ref>[^" ]*)" "(?P<ua>[^" ]*)"')
bots = ('Googlebot', 'Bingbot', 'GPTBot', 'ClaudeBot', 'PerplexityBot')
counts = Counter(); malformed = 0
with open('access.log', encoding='utf-8', errors='replace') as log:
    for line in log:
        match = line_re.match(line)
        if not match: malformed += 1; continue
        row = match.groupdict(); ua = row['ua']
        group = next((bot for bot in bots if bot.lower() in ua.lower()), 'human/other')
        path = row['request'].split('?', 1)[0]
        counts[(group, row['status'], path)] += 1
for key, total in counts.most_common(30): print(total, *key)
print('malformed=', malformed)

حوّل الطلبات إلى قرارات زحف

أنشئ تقريرًا على مستوى URL يضم hits والأيام الفريدة وفئات status والحجم ومجموعات البوتات. أعط الأولوية لصفحات تتلقى 4xx/5xx أو redirects متسلسلة، بينما لا تصل الزواحف إلى canonicals مهمة.

# أعلى URLs غير 200 للبوتات
awk -F'"' '/Googlebot|GPTBot|ClaudeBot|PerplexityBot/ { split($1,a," "); if (a[9] !~ /^2/) print a[9], $2 }' access.log | sort | uniq -c | sort -nr | head -50

# جدول قرار مختصر
status >= 500  -> أصلح موثوقية origin
status == 404  -> أصلح الروابط أو أعد المورد canonical
status 301/302 -> أزل السلاسل وحدّث الروابط الداخلية

الهدف ليس dashboard ضخمًا؛ الهدف قائمة قابلة للدفاع بهدر crawl budget والصفحات المهمة التي لا تصل إليها الزواحف.