كيف تفكّر ببايثون لحل مشكلة SEO؟

45 دقيقة تقريبًا

ما تحتاج أن تعرفه قبل هذا الدرس
  • Variable
  • String
  • Integer
  • Float
  • Boolean
  • List
  • Dictionary
  • print
  • type
  • key
  • index
  • append
  • len
  • if
  • else
  • elif
  • Comparison Operators
  • and
  • or
  • not
  • for
  • while
  • break
  • continue
  • Function
  • def
  • Parameter
  • Argument
  • return
  • open
  • with
  • read
  • write
  • strip
  • startswith
  • endswith
  • import
  • urlparse
  • csv
  • reader
  • DictReader
  • writer
  • DictWriter
  • fieldnames
  • pandas
  • DataFrame
  • read_csv
  • head
  • columns
  • shape
  • value_counts
  • isna
  • notna
  • sort_values
  • contains
  • to_csv
  • set
  • lower
  • rstrip
  • replace
  • split
  • duplicated
  • drop_duplicates
  • rename
  • merge
  • groupby
ما يضيفه هذا الدرس
  • Input/Processing/Output
  • Pseudocode
  • Debugging
  • Sanity Check
  • try
  • except

يحسب العدّاد في الأعلى تمارين الدرس، وعددها 12، أما القراءة وتشغيل الأمثلة فلا تُحتسب.

إحنا وصلنا لنقطة مختلفة شوية عن الدروس اللي فاتت.

لحد دلوقتي كنا كل درس بنتعلم حاجة جديدة:

وكل واحدة فيهم خدنا عليها أمثلة وطبقناها في SEO.

لكن لو أنا دلوقتي اديتك ملف Crawl فيه 50,000 URL وقلتلك:

ملحوظة
طلعلي صفحات 200 اللي مفيهاش Title، ورتبها حسب عدد الكلمات، واحفظ النتيجة في ملف جديد.

هل أول حاجة المفروض تعملها إنك تبدأ تكتب كود؟

لا. ودي بالظبط الفكرة اللي هنشتغل عليها في الدرس ده.

المشكلة مش إنك تعرف أمر بايثون أو متعرفوش. المشكلة الأهم إنك تعرف:

ملحوظة
أنا محتاج أعمل إيه أصلًا؟

وبعدها:

ملحوظة
إيه الأدوات اللي اتعلمتها وتقدر تحل المشكلة دي؟

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

وده درس مهم جدًا قبل المشروع النهائي.

متبدأش من الكود، ابدأ من المشكلة

خلينا ناخد المثال ده:

ملحوظة
عندك ملف Crawl وعايز تعرف صفحات 200 اللي Title بتاعها مفقود.

المبتدئ غالبًا يشوف المشكلة ويبدأ يدور:

وبعدها ينسخ أي كود يلاقيه. ممكن الكود يشتغل. لكن لو تغير اسم عمود واحد، أو شكل الملف اختلف، غالبًا هيقف ومش هتبقى فاهم ليه.

إحنا عايزين طريقة تانية. قبل أي كود، اسأل نفسك:

البيانات موجودة فين؟

عندي ملف crawl.csv.

إيه المعلومات اللي محتاجها؟

محتاج URL وStatus وTitle.

إيه الشرط؟

عندي شرطين: Status = 200 وTitle مفقود.

عايز أعمل إيه بالنتيجة؟

مثلًا: أطبعها، أراجعها، أحفظها في CSV جديد.

إذن المشكلة اللي كانت شكلها كبير بدأت تتحول لحاجات صغيرة.

المشكلة اتحولت لخطوات
  1. 1اقرأ الملف.
  2. 2تأكد من الأعمدة.
  3. 3فلتر Status 200.
  4. 4فلتر Title المفقود.
  5. 5اعرض النتيجة.
  6. 6احفظها.

دلوقتي فقط نبدأ نفكر في الكود.

حول المشكلة لجمل بسيطة الأول

قبل ما تكتب:

لازم تكون قادر تقول لنفسك بالعربي:

ملحوظة
هات الصفوف اللي Status فيها 200 والـ Title فيها مفقود.

لو الجملة نفسها مش واضحة في دماغك، الكود غالبًا هيكون أصعب.

خلي بالك من النقطة دي. بايثون مش هي اللي تحدد لك المشكلة. إنت الأول بتحدد المنطق. وبعدها بتحوله إلى بايثون.

يعني عندنا Status = 200 تتحول إلى df["Status"] == 200.

وعندنا Title مفقود تتحول إلى df["Title"].isna().

وعندنا الشرط الأول والشرط الثاني فتتحول في Pandas إلى &. فيبقى:

أنت هنا مش حفظت سطر. أنت بنيته. وده فرق كبير جدًا.

متحلش المشكلة الكبيرة مرة واحدة

خلينا نقول إن عندك المهمة دي:

ملحوظة
اقرأ Crawl، واستخرج صفحات 200، ومنهم الصفحات اللي Title مفقود، والصفحات اللي عدد كلماتها أقل من 100، واعرف عدد صفحات كل Status Code، وبعدها احفظ كل مشكلة في ملف منفصل.

لو حاولت تكتب البرنامج كله مرة واحدة، احتمال كبير جدًا تغلط ومتعرفش الغلط فين.

الأفضل تكسر المهمة. أول حاجة:

لو النتيجة ظهرت صح، نكمل. بعدها:

لو أسماء الأعمدة موجودة زي ما متوقعين، نكمل. بعدها نجرب أول شرط:

اشتغل؟ نكمل. بعدها:

اشتغل؟ نكمل. بعدها نحفظ:

إنت كده بنيت البرنامج جزء جزء. ولو حاجة غلط حصلت، هتعرف تقريبًا في أي خطوة حصلت.

دي قاعدة مهمة جدًا في التعلم

متكتبش 50 سطر كود وبعدها تشغلهم لأول مرة.

بدل كده
  1. 1اكتب جزء صغير.
  2. 2شغله.
  3. 3شوف الناتج.
  4. 4افهمه.
  5. 5وبعدين كمل.

وده سبب إننا من أول الكورس بنطبع النتائج. مش لأن print() هي الحل النهائي. لكن لأنها بتخليك تشوف بايثون بتعمل إيه.

أول Trick: افحص البيانات قبل ما تحللها

افترض إن حد بعتلك ملف crawl.csv وبدأت على طول تعمل:

لكن العمود أصلاً اسمه Status Code. الكود هيطلع Error.

أو ممكن Status يكون مكتوب فيه OK وNot Found مش أرقام.

أو ممكن الملف أصلاً مش هو الملف اللي فاكره.

علشان كده أول ما تقرأ Data Set جديدة، اعمل فحص سريع. مثلًا:

النتائج ممكن تكون:

ملحوظة
رقم Pandas اللي شغالة هنا (جوه هذا الموقع) بتطبع dtype='str' بدل dtype='object' للأعمدة النصية — تفصيلة اتشرحت في الدرس السابق. المصدر هنا بيوريك الفكرة العامة، مش رقم نسخة معينة.

الأربع حاجات دول بيجاوبوا على أسئلة مهمة.

head() بيجاوب على سؤال: البيانات شكلها إيه؟

print(df.head()) بيديك أول خمس Rows. منهم تقدر تشوف:

  • البيانات اتقرت صح؟
  • أسماء القيم منطقية؟
  • الأعمدة فيها البيانات اللي متوقعها؟
  • فيه حاجة غريبة؟

columns بيجاوب على سؤال: عندي إيه أصلًا؟

print(df.columns) مهم جدًا قبل ما تعمل df["Title"]. لأن ممكن العمود يكون اسمه Title 1 أو Page Title. إنت مش مفروض تخمن. بص على الملف الأول.

shape بيجاوب على سؤال: حجم البيانات قد إيه؟

print(df.shape) لو الناتج (50000, 20) يبقى عندك 50,000 Row و20 Column. ودي معلومة مهمة.

تخيل إنك كنت متوقع 50,000 URL والنتيجة (180, 20). هل تكمل التحليل؟

الأفضل الأول تسأل نفسك:

ملحوظة
ليه الملف فيه 180 Row بس؟

ممكن تكون صدرت جزء من Crawl مش كله. يعني الفحص هنا مش مجرد بايثون. ده جزء من صحة تحليل SEO نفسه.

dtypes بيجاوب على سؤال: بايثون شايفة البيانات إزاي؟

print(df.dtypes) مثلًا يورّيك Status int64 وWords int64 وTitle object.

ده بيساعدك تعرف هل المقارنة اللي هتعملها منطقية.

لو Status رقم: df["Status"] == 404. لكن لو Status نص: df["Status"] == "404". ممكن تحتاج تتعامل بشكل مختلف. متفترضش نوع البيانات. اتأكد.

ثاني Trick: اختبر الفلتر قبل ما تعتمد عليه

افترض إنك كتبت:

الكود اشتغل بدون Error. هل ده معناه إن النتيجة صح؟

مش بالضرورة. الكود ممكن يشتغل ويطلع نتيجة غلط بسبب إن الشرط نفسه غلط.

علشان كده اطبع عينة:

وشوف بنفسك. هل الصفحات فعلًا Status بتاعها 200؟ هل الـ Title فعلًا مفقود؟

لو النتيجة 0 Rows:

وظهر (0, 5)، متفترضش فورًا إن الموقع مفيهوش Missing Titles.

ممكن يكون السبب مثلًا إن الخلايا الفاضية مكتوب فيها Missing بدل ما تكون NaN. يبقى شرط df["Title"].isna() مش هيطلعهم. إذن لازم تبص على البيانات نفسها.

الكود اشتغل مش معناها التحليل صح

دي من أهم النقاط في الدرس. بايثون ممكن تشغل الكود من غير أي Error. لكن إنت تكون سألت السؤال الغلط.

مثال:

الكود صحيح. لكن لو هدفك: هات صفحات 200. فالشرط غلط. ليه؟ لأن Status != 404 يعني: هات أي حاجة مش 404.

فممكن يجيب 200 و301 و302 و500 و503. إنت كنت محتاج:

الاتنين كود صحيح من ناحية بايثون. لكن واحد بس بيجاوب السؤال اللي إنت عايزه. وده السبب إن الـ SEO Mindset أهم من مجرد كتابة Syntax صحيح.

اسأل نفسك: أنا بفلتر على إيه؟

خلينا ناخد مثال. عندك:

URLStatusIndexability
/page-1/200Indexable
/page-2/200Non-Indexable
/page-3/404Non-Indexable
/page-4/301Non-Indexable

لو المطلوب: هات الصفحات Non-Indexable. ممكن تعمل:

مثال
الناتج المتوقع
        URL  Status   Indexability
1  /page-2/     200  Non-Indexable
2  /page-3/     404  Non-Indexable
3  /page-4/     301  Non-Indexable

صح؟ آه. لكن لو السؤال الحقيقي: هات صفحات 200 اللي Non-Indexable. يبقى لازم تضيف:

فيبقى:

مثال
الناتج المتوقع
        URL  Status   Indexability
1  /page-2/     200  Non-Indexable

الفرق هنا مش بايثون. الفرق إنك حددت السؤال بدقة.

ثالث Trick: ابدأ بعينة صغيرة

لو عندك ملف فيه 1,000,000 Row، وإنت لسه بتبني الكود، مش لازم كل تجربة تكون على مليون Row.

ممكن تعمل نسخة صغيرة من البيانات وتشتغل عليها الأول. مثلًا:

كده عندك أول 100 Row. اشتغل عليهم. جرب الشروط. راجع النتائج. وبعد ما تتأكد إن المنطق صح، طبقه على DataFrame كاملة.

دي مش بس طريقة أسرع. دي طريقة أسهل في اكتشاف الأخطاء.

رابع Trick: اطبع العدد قبل وبعد الفلترة

افترض إن عندك df وفيها 50,000 Row. عايز تطلع صفحات 404.

بدل ما تحفظ الملف وخلاص، اعمل:

وبعد الفلترة:

الناتج مثلًا:

هنا أنت بقيت فاهم إيه اللي حصل.

يعني إيه len() هنا؟

إحنا استخدمنا len() قبل كده. مع List مثلًا:

مثال
الناتج المتوقع
3

مع DataFrame، print(len(df)) هتجيب عدد الـ Rows. فلو عندك 50000 Row، الناتج 50000. وده مفيد جدًا في المراجعة.

اعمل Sanity Check

مصطلح مهم في طريقة العمل: Sanity Check. مش محتاج تحفظ ترجمة حرفية ليه. الفكرة ببساطة:

ملحوظة
اعمل فحص سريع تتأكد بيه إن النتيجة منطقية قبل ما تكمل.

مثلًا عندك موقع فيه 20,000 URL. عملت تحليل وطلع:

هل ممكن يكون ده صحيح؟ ممكن. لكن الرقم كبير جدًا ويستحق إنك توقف وتراجع. اطبع:

وشوف. ممكن تكتشف إنك استخدمت اسم عمود غلط. أو إن كل القيم اتحولت إلى NaN أثناء القراءة. أو إن Export نفسه فيه مشكلة.

الـ Sanity Check مش أمر بايثون. دي طريقة تفكير.

خامس Trick: خليك واضح في أسماء المتغيرات

ممكن تكتب:

الكود هيشتغل. لكن بعد 50 سطر:

هتسأل نفسك: x كانت إيه؟ الأفضل:

ومثلًا بدل:

اكتب:

وبدل:

اكتب:

إنت مش بتدفع فلوس على عدد الحروف. اكتب اسم يخليك تفهم الكود. خصوصًا وإنت لسه بتتعلم.

سادس Trick: متختصرش الكود قبل ما تفهمه

ممكن تكتب:

السطر ممكن يكون صحيح. لكن لو إنت لسه بتتعلم، إيه المكسب؟

خلينا نفك نفس العملية:

النتيجة واحدة تقريبًا. لكن النسخة الثانية تقدر تمشي فيها خطوة خطوة. تقدر تطبع print(pages_200) وبعدين print(low_word_pages). ولو حصل خطأ تعرف فين.

لما تبقى فاهم الكود كويس، تقدر تختصر لو الاختصار فعلًا بيخلي الكود أفضل. لكن متختصرش لمجرد إن السطر الأقصر شكله أذكى.

سابع Trick: الفرق بين Loop وPandas

دي من الأسئلة المهمة جدًا. إحنا اتعلمنا Loops. وبعدين Pandas. هل معنى كده إن أي بيانات SEO نستخدم فيها Pandas؟

لا. اختيارك بيعتمد على المشكلة.

إمتى Loop تكون منطقية؟

لو عندك List بسيطة:

وعايز تطبع كل URL:

مثال
الناتج المتوقع
https://example.com/
https://example.com/blog/
https://example.com/product/

ده طبيعي جدًا. أو لو كل URL محتاج عملية مستقلة. مثلًا في المستقبل لما نبدأ نرسل Request لكل صفحة:

هنا Loop هتكون جزء طبيعي جدًا من الحل.

إمتى Pandas تكون أفضل؟

لو عندك جدول:

URLStatusTitleWords
/page-1/200Page 1500
/page-2/404Page 20
/page-3/200—80

وعايز Filter وSort وCount واختيار أعمدة وMissing Values، هنا Pandas مناسبة جدًا. مثلًا:

أسهل من بناء Loop يدويًا.

متقولش Loop سيئة وPandas كويسة

الاتنين أدوات. لو عندك مسمار، الشاكوش مفيد. لو عندك مسمار ربط، المفك مفيد. مفيش أداة أفضل بشكل مطلق. السؤال:

ملحوظة
إيه الأداة المناسبة للمشكلة اللي قدامي؟

إمتى أعمل Function؟

إحنا اتعلمنا Functions، لكن السؤال المهم: إمتى فعلًا أحتاج واحدة؟

افترض إنك كتبت:

مرة واحدة. هل لازم تعمل Function؟ لا.

لكن افترض إنك عندك 5 ملفات، وكل مرة عايز تعمل نفس الفحص. هنا ممكن تعمل:

مثال
الناتج المتوقع
Rows: 4
Columns: 5
        URL  Status   Title   Indexability  Words
0  /page-1/     200  Page 1      Indexable    500
1  /page-2/     404  Page 2  Non-Indexable      0
2  /page-3/     200     NaN      Indexable     80
3  /page-4/     301  Page 4  Non-Indexable     20

وبعدين inspect_data(df). لو عندك DataFrame تانية: inspect_data(search_console_df). هنا الـ Function منطقية لأن عندك عملية متكررة.

قاعدة بسيطة للـ Functions

لو لقيت نفسك بتنسخ نفس الكود أكتر من مرة، اسأل نفسك:

ملحوظة
هل الجزء ده ينفع يبقى Function؟

مش معنى كده إن كل 3 أسطر لازم تحولهم Function. إحنا مش بنعمل Functions علشان الكود يبان احترافي. بنعملها لما تساعدنا ننظم الشغل أو نمنع التكرار.

إمتى أستخدم List ومتى DataFrame؟

خد المثال ده:

عندي مجموعة URLs فقط. List مناسبة جدًا.

لكن لو كل URL معاه Status وTitle وWords وIndexability، بقى عندنا جدول. هنا DataFrame غالبًا هتبقى أفضل.

يعني: قائمة قيم بسيطة → List. أما Rows + Columns → DataFrame.

مش قانون ثابت في بايثون كلها، لكن طريقة جيدة جدًا تفكر بيها في المرحلة الحالية.

متستخدمش أداة كبيرة لمشكلة صغيرة

عايز تعرف هل URL واحد فيه /blog/. محتاج Pandas؟ لا.

مثال
الناتج المتوقع
Blog

كفاية.

عندك 50,000 URL داخل CSV ومعاهم 20 عمود وعايز تطلع صفحات Blog وتحلل Status؟ هنا Pandas منطقية.

اقرأ الـ Error بدل ما تقفل الكود

في بداية تعلم بايثون طبيعي جدًا تشوف Errors. المشكلة إن أول رد فعل بيكون: الكود مش شغال.

لكن الـ Error غالبًا بتقولك معلومة مهمة. مثال:

مثال

لكن العمود الموجود اسمه Status. ممكن يظهر KeyError: 'Status Code'.

بدل ما تقول: Pandas بايظة. اقرأ KeyError وبعدها 'Status Code'. الرسالة بتقول إن المفتاح أو العمود اللي طلبته مش موجود بالشكل ده.

أول حاجة تعملها:

وتشوف الاسم الحقيقي.

مثال تاني على Error

كتبت:

مثال

لكن الملف مش موجود في المكان الحالي. ممكن يظهر FileNotFoundError.

إيه أول سؤال؟

  • هل اسم الملف صحيح؟
  • هل الملف في نفس المجلد؟
  • هل كتبت الـ Path صح؟

مش محتاج تحفظ أسماء الـ Errors كلها. إحنا عايزينك تبدأ تتعامل معاها كمعلومة، مش كحائط وقف قدامك.

طيب لو عايز البرنامج يكمّل حتى لو حصل Error؟

لحد دلوقتي كنا بنقرأ الـ Error ونصلّح الكود. لكن أحيانًا الـ Error مش غلطة في الكود، الغلطة في البيانات نفسها.

مثلًا عندك عمود Word Count جاي من أداة، ومعظم القيم أرقام، لكن فيه صفحات مكتوب فيها N/A أو -. ولسه إحنا عايزين نحوّلهم لأرقام بـ int():

مثال

هيظهر ValueError: invalid literal for int() with base 10: 'N/A'

والمشكلة الأكبر إن البرنامج وقف عند تاني قيمة. القيم اللي بعدها معدّاش عليها أصلًا. تخيل ده بيحصل مع 50,000 صف، والبرنامج وقف عند الصف رقم 12.

هنا بنستخدم try وexcept:

مثال
الناتج المتوقع
Not a number: N/A
Not a number: -
[850, 1200, 300]

اقرأها كده:

ملحوظة
جرّب تحوّل القيمة لرقم. لو حصل ValueError، متقفش، اطبع القيمة وكمّل على اللي بعدها.

السطر اللي جوه try هو اللي ممكن يعمل مشكلة. ولو المشكلة حصلت فعلًا، بايثون بتنط على طول للي جوه except، وبعدها الـ Loop بتكمّل عادي.

ليه كتبنا ValueError بالظبط؟

ممكن تكتب except: لوحدها من غير نوع الـ Error، والكود هيشتغل. لكن كده إنت بتقول لبايثون: أي مشكلة تحصل، اسكت عنها.

ولو فيه غلطة تانية خالص في الكود، زي اسم متغير مكتوب غلط، مش هتعرف بيها أبدًا. والبرنامج هيطلّع نتيجة غلط وإنت فاكرها صح.

وده عكس كل اللي اتكلمنا عليه في الدرس ده. إحنا عايزين نعرف إيه اللي حصل، مش نخبّيه.

علشان كده حدد الـ Error اللي متوقعه، ولما يحصل اطبعه أو سجّله. زي ما عملنا: طبعنا القيم اللي معرفناش نحوّلها، علشان تبقى عارف إن فيه صفحتين بياناتهم ناقصة.

نفس الفكرة مع الملفات

مثال
الناتج المتوقع
File not found: crawl.csv

بدل ما البرنامج يقف برسالة طويلة، بيقولك بوضوح إيه المشكلة.

من غير ما تشغّل، توقّع الناتج:

توقّع الناتج

إيه اللي هيتطبع؟

ليه ده مهم دلوقتي بالذات؟

في المرحلة الجاية هنبدأ نبعت Requests لصفحات حقيقية. صفحة ممكن تعمل Timeout، وسيرفر ممكن ميردّش. من غير try وexcept، صفحة واحدة واقعة هتوقف البرنامج كله، وإنت لسه في أول 100 صفحة من 5,000.

لما يحصل Error، متغيرش خمس حاجات مرة واحدة

دي نقطة مهمة. افترض إن الكود مش شغال. متعملش الآتي مرة واحدة:

  • تغير اسم الملف.
  • تغير اسم العمود.
  • تغير الشرط.
  • تغير طريقة القراءة.
  • تغير المكتبة.

وبعدين تجرب. لو اشتغل، مش هتعرف أصلًا إيه اللي كان غلط.

غير حاجة واحدة. جرب. شوف النتيجة.

ودي نفس طريقة التفكير اللي بنستخدمها في Technical SEO. لو عندك مشكلة وغيّرت 10 حاجات مرة واحدة، حتى لو المشكلة اتحلت، إنت مش عارف إيه اللي حلها.

افصل بين Input وProcessing وOutput

دي من أحسن الطرق اللي تخليك تفهم أي برنامج هنعمله بعد كده. أي مهمة تقريبًا ممكن تقسمها إلى:

أي مهمة
  1. 1Input
  2. 2Processing
  3. 3Output

Input

البيانات جاية منين؟ مثلًا crawl.csv أو urls.txt أو بعد كده Website وAPI وSearch Console.

في مثالنا:

ده جزء الـ Input.

Processing

هنعمل إيه بالبيانات؟ مثلًا:

ده Processing. إحنا أخدنا البيانات وطبقنا عليها منطق.

Output

عايز النتيجة فين؟ ممكن:

أو:

ده Output.

ليه التقسيم ده مهم؟

لأن لما البرنامج يكبر، تقدر تسأل: المشكلة في Input؟ يعني الملف متقراش صح؟

ولا المشكلة في Processing؟ الشرط غلط؟

ولا المشكلة في Output؟ النتيجة صح لكن الملف متحفظش؟

بدل ما تبص على البرنامج كله ككتلة واحدة.

مثال كامل بطريقة التفكير

المهمة:

ملحوظة
عندك Crawl، وعايز تعرف صفحات Blog اللي Status بتاعها 200 والـ Title بتاعها مفقود.

متكتبش كود الأول. نفكر.

البيانات جاية منين؟

crawl.csv

محتاجين أعمدة إيه؟

URL وStatus وTitle

عندنا كام شرط؟

ثلاثة: URL يحتوي على /blog/، وStatus = 200، وTitle مفقود.

عايزين النتيجة إيه؟

CSV جديد: blog-missing-titles.csv. دلوقتي نكتب الكود.

مثال
الناتج المتوقع
                                URL  Status Title Indexability  Words
3  https://example.com/blog/python/     200   NaN    Indexable   1500
Total URLs: 8
Blog missing titles: 1

شايف الفرق؟ السطر الكبير جه في الآخر. الأول كان عندنا المنطق نفسه واضح.

لكن قبل ما أعتمد النتيجة، نعمل فحص عينة، ونبص على أول عشر نتائج قبل ما نثق فيها، وده اللي طبّقناه فعلًا في الكود فوق بطباعة العدد قبل وبعد الفلترة.

التفكير قبل الكود ممكن يكون ورقة وقلم

مش لازم كل التفكير يحصل جوه بايثون. لو المهمة كبيرة، ممكن تكتب الأول:

دي مش بايثون. ودي مش مشكلة. بالعكس. إنت هنا فصلت حل المشكلة عن كتابة الكود. بعدها كل خطوة تحولها إلى بايثون.

وده يقودنا لفكرة اسمها Pseudocode

هنستخدم الكلمة دي كتير قدام. هي مش كود بايثون حقيقي. هي طريقة تكتب بيها منطق البرنامج بلغة بسيطة قبل ما تنفذه. مثلًا:

ده Pseudocode. لو حطيته في بايثون بالشكل ده، مش هيشتغل. لكن هو بيساعدك تبني البرنامج.

وبعدها نترجم: اقرأ الملف إلى df = pd.read_csv("crawl.csv"). وترجم هات Status 200 إلى pages_200 = df[df["Status"] == 200]. وهكذا.

متفكرش في Syntax وإنت لسه مش عارف الحل

دي غلطة مشهورة. الشخص عنده مشكلة، وأول حاجة يفكر فيها: اسم الـ Function إيه؟

لكن لسه مش عارف هو أصلًا محتاج Function تعمل إيه. الترتيب الأفضل:

الترتيب الصح
  1. 1المشكلة
  2. 2المنطق
  3. 3الخطوات
  4. 4الأداة
  5. 5Syntax

مش: Syntax، وبعدين أشوف هعمل بيها إيه.

مثال: عندك 100,000 URL وفيه Crawl Trap محتمل

مش هندخل دلوقتي في تشخيص Crawl Trap نفسه، لكن خلينا نفكر في البيانات.

عندك CSV فيه URL وStatus، ولاحظت URLs كثيرة بالشكل:

مهمتك الأول: أعرف عدد URLs اللي فيها Parameters. نفكر.

البيانات: CSV. العمود: URL. الشرط: الرابط يحتوي على ?. إذن ممكن نستخدم:

لكن هنا ظهر عندنا شكل غريب: "\?". ومش عايز أدخلك في موضوع Regular Expressions في الدرس ده.

فممكن بدل كده نرجع للحاجة اللي إنت فاهمها بالفعل. نضيف عمود منطقي باستخدام apply()؟ برضه دي حاجة جديدة ومش محتاجينها.

إذن إيه القرار الصح حاليًا؟

القاعدة
ممنعقدش الحل لمجرد إننا عايزين نستخدم Pandas في كل حاجة.

ممكن نستخدم Loop بسيطة على الـ URLs لو ده أوضح بالنسبة للمستوى الحالي. مثلًا:

مثال
الناتج المتوقع
2

النقطة هنا مهمة جدًا. إحنا اخترنا أداة من الأدوات اللي فاهمينها بالفعل. مش لازم الحل الأقصر يكون هو الأفضل للتعلم أو للمهمة الحالية.

لو فيه مشكلة نقدر نحلها بحاجة اتعلمناها، ومفيش سبب قوي ندخل Concept جديد، استخدم اللي فاهمه.

ملحوظة
وفاكر regex=False من درس Pandas؟ ده كمان حل بسيط بأداة إنت فاهمها: df[df["URL"].str.contains("?", regex=False)]

في المشروع النهائي هنحاول نشتغل بالأدوات اللي اتعلمناها. مش هنستعرض كل مكتبات بايثون في مشروع واحد.

Trick مهم: متعدلش الملف الأصلي في البداية

عندك crawl.csv وده Export أصلي من الأداة. متبدأش إنك تكتب النتيجة فوق نفس الملف. مثلًا متعملش من البداية:

لو إنت بتجرب. الأفضل:

أو 404-pages.csv وmissing-titles.csv.

ليه؟ علشان الملف الأصلي يفضل موجود. لو غلطت في الفلترة، تقدر ترجع للبيانات الأصلية. ودي عادة مهمة جدًا في تحليل البيانات.

Trick مهم: احتفظ بالأعمدة اللي تساعدك في المراجعة

افترض إنك بتطلع Missing Titles. هل تحفظ URL فقط؟ ممكن.

لكن أثناء المراجعة ممكن يكون مفيد جدًا تحتفظ بـ URL وStatus وTitle وIndexability بدل URL فقط.

ليه؟ علشان لما تفتح التقرير، يبقى عندك Context يساعدك تتأكد من النتيجة. مثلًا:

وبعدها:

فكر في الـ Output من زاوية الشخص اللي هيراجعه بعد كده. مش من زاوية: أنا قدرت أطلع List وخلاص.

Trick مهم: متعتبرش كل حاجة Issue لمجرد إن الأداة قالت كده

خلينا ناخد مثال مهم. إنت طلعت 200 Non-Indexable Pages. هل كل صفحة فيهم مشكلة SEO؟

لا. ممكن تكون صفحة المفروض أصلًا تكون noindex.

إذن بايثون هنا بتقول لك: دي الصفحات اللي تنطبق عليها الشروط. لكن مش بالضرورة: دي مشاكل ويجب إصلاحها.

دي نقطة جوهرية جدًا. بايثون بتحلل البيانات حسب المنطق اللي إنت كتبته. هي مش بديل عن قرار الـ SEO. مثلًا:

ده معناه: هات الصفحات اللي حالتها 200 ومش Indexable. مش معناه: كل الصفحات دي أخطاء. بعدها ييجي دورك كمتخصص SEO.

بايثون تجيب لك Candidates مش Decisions

خلي دي قاعدة عندك. مثلًا Pages under 100 words، ممكن تطلعها باستخدام:

هل أي صفحة أقل من 100 كلمة تعتبر Thin Content؟ لا. ممكن تكون:

  • صفحة Contact.
  • صفحة Login.
  • صفحة Utility.
  • صفحة منتج طبيعتها قصيرة.
  • صفحة مقصود تكون مختصرة.

إذن بايثون تقول: عندي 45 صفحة أقل من 100 كلمة. لكن إنت اللي تقرر هل ده Issue ولا لأ.

دي واحدة من أهم الفروق بين Automation وSEO Judgment.

متخليش الـ Automation تلغي عقلك

هدفنا من بايثون: توفير الوقت، تقليل العمل اليدوي، اكتشاف Patterns، تحليل Data كبيرة. مش إلغاء التفكير.

لو عملت Script تقول: أي صفحة أقل من 300 كلمة = Bad SEO. إنت بنيت Rule ضعيفة بسرعة أكبر فقط.

يعني الأتمتة مش هتصلح التفكير الغلط. هتخليه أسرع.

مثال تاني: 404 مش معناها Redirect تلقائي

طلعنا:

هل معنى كده نعمل Redirect لكل 404؟ لا. في 404 ممكن:

  • تحتاج Redirect.
  • تفضل 404 طبيعي.
  • تكون URL غلط داخليًا.
  • تكون Backlink قديم.
  • تحتاج 410 في حالة معينة.
  • تحتاج إصلاح Internal Link فقط.

بايثون طلعت المجموعة. قرار العلاج محتاج Context. دي طريقة التفكير اللي عايزين نفضل محافظين عليها طول الكورس.

افصل الاكتشاف عن القرار

ممكن نقسم شغل SEO Automation إلى مرحلتين:

مرحلتين
  1. 1Detection
  2. 2Decision

يعني بايثون تساعدك في Detection، مثلًا: هات 404، هات Missing Titles، هات Redirects، هات Parameters.

وبعدها ييجي Decision، ودي مرحلة محتاجة فهم SEO. مثلًا:

  • هل 404 دي مشكلة؟
  • هل محتاجة Redirect؟
  • هل الصفحة المفروض Indexable؟
  • هل الـ Parameter URL فعلاً مشكلة؟

مش كل Detection يتحول تلقائيًا إلى Fix.

دلوقتي ناخد مشكلة من البداية للنهاية

المهمة: عندك Crawl من Screaming Frog، وعايز تعمل تقرير سريع عن الصفحات اللي تحتاج مراجعة.

الملف: crawl.csv. الأعمدة: URL وStatus وTitle وIndexability وWords.

قبل ما نكتب الكود، هنحدد اللي عايزينه. عايزين:

  1. صفحات 404.
  2. صفحات 301.
  3. صفحات 200 بدون Title.
  4. صفحات 200 غير Indexable.
  5. صفحات 200 أقل من 100 كلمة.
  6. عدد كل Status Code.

دلوقتي نبني البرنامج خطوة خطوة.

الخطوة الأولى: اقرأ البيانات

منشغلش أي تحليل لسه.

الخطوة الثانية: افحص الملف

لو كل حاجة منطقية، نكمل.

الخطوة الثالثة: افهم حجم الملف

الناتج مثلًا:

دلوقتي عندنا Baseline.

الخطوة الرابعة: Status Codes

الناتج مثلًا:

قبل حتى ما تعمل تقارير، أنت أخدت فكرة سريعة جدًا عن الـ Crawl.

الخطوة الخامسة: صفحات 404

الناتج:

بعدها شوف عينة:

الخطوة السادسة: Redirects

الخطوة السابعة: Missing Titles

راجع عينة:

الخطوة الثامنة: Non-Indexable 200

الخطوة التاسعة: صفحات قليلة الكلمات

لكن افتكر: النتيجة دي Pages to review. مش Pages that must be fixed.

الخطوة العاشرة: احفظ التقارير

دلوقتي البرنامج خلص.

هل نحوله Function؟

ممكن. لكن قبل ما نحوله Function، اسأل نفسك:

ملحوظة
أنا محتاج أشغل التحليل ده على ملفات مختلفة كتير؟

لو الإجابة نعم، ممكن نبدأ بعد كده ننظمه. مثلًا:

مثال
الناتج المتوقع
        URL  Status   Title   Indexability  Words
0  /page-1/     200  Page 1      Indexable    500
1  /page-2/     404  Page 2  Non-Indexable      0
2  /page-3/     200     NaN      Indexable     80
3  /page-4/     301  Page 4  Non-Indexable     20

لكن لو المشروع كله بيقرأ ملف واحد فقط، مش لازم تحول كل خطوة Function. خلي التنظيم يخدم المشكلة. مش المشكلة تخدم شكل الكود.

Mindset مهم: كل خطوة لازم تعرف دخلها وخرجها

خد:

قبل السطر: Input هو df. بعد السطر: Output هو broken_pages.

إيه الفرق؟ df فيها كل البيانات. broken_pages فيها فقط الصفوف اللي Status بتاعها 404.

لو إنت مش قادر تشرح السطر ده بالكلام، ارجع وافهمه قبل ما تكمل.

حاول تتوقع النتيجة قبل التشغيل

دي من أفضل طرق التعلم. عندنا:

لو كتبنا Loop، حاول تتوقع النتيجة قبل ما تضغط Run.

نفس الفكرة مع SEO. لو الملف فيه:

URLStatus
/page-1/200
/page-2/404
/page-3/404
/page-4/301

وعملنا:

قبل التشغيل، إنت عارف إن المفروض النتيجة صفحتين. لو الكود طلع 4، فيه مشكلة. لو طلع 0، فيه مشكلة.

التوقع بيخليك مشغل عقلك مع الكود.

Trick: اعمل Dataset صغيرة بإيدك لاختبار المنطق

دي حركة مفيدة جدًا. قبل ما تجرب على Crawl حقيقية، ممكن تعمل ملف صغير test.csv فيه:

إنت عارف بالظبط النتيجة المفروض تكون إيه.

Missing Titles: /page-3/. 404: /page-2/. 301: /page-4/. Low Words: /page-3/.

مثال
الناتج المتوقع
        URL  Status Title Indexability  Words
2  /page-3/     200   NaN    Indexable     80

لما الكود ينجح على البيانات الصغيرة اللي إنت عارف نتيجتها، بعد كده جربه على الملف الحقيقي.

الفرق بين Debugging وGuessing

لو الكود مش شغال، عندك طريقتين.

الطريقة الأولى: أغير حاجات عشوائي لحد ما يشتغل. دي Guessing.

الطريقة الثانية:

Debugging
  1. 1أعرف آخر جزء اشتغل صح.
  2. 2أعرف أول جزء طلع غلط.
  3. 3أفحص القيم.
  4. 4أغير الحاجة اللي ليها علاقة بالمشكلة.

إحنا عايزين نعود نفسنا على الطريقة التانية.

مثال Debugging بسيط

عندك:

مثال
الناتج المتوقع
Empty DataFrame
Columns: [URL, Status]
Index: []

والنتيجة: Empty DataFrame. إنت متأكد إن الملف فيه 404. متغيرش الشرط فورًا. جرب:

مثال
الناتج المتوقع
0           200 OK
1           200 OK
2    404 Not Found
Name: Status, dtype: str

يمكن تلاقي إن القيم مش 404 بل 404 Not Found. إنت كده اكتشفت المشكلة من البيانات.

مثال تاني

كتبت:

وظهر KeyError: 'Title'. اعمل:

ممكن تلاقي Title 1. إذن المشكلة في اسم العمود. مش في Pandas.

خد بالك من المسافات

ممكن Header يكون Title فيه Space في الآخر. إنت شايفه بعينك Title. لكن بايثون شايفاه "Title ". ودي قيمة مختلفة عن "Title".

دلوقتي مش هنبدأ Cleaning متقدم للبيانات. لكن عايزك تعرف إن مشاكل شكل البيانات نفسها موجودة.

وده سبب إضافي إن print(df.columns) مهمة.

Tricks عملية قبل أي SEO Analysis

لما تستلم Data Set جديدة، ممكن تعمل الأول:

وبعدها لو عندك Status:

لو عندك Indexability:

ده مش Script كامل للتحليل. ده مجرد استكشاف أولي للبيانات. ودي عادة ممتازة.

متكتبش شرط على عمود قبل ما تعرف القيم اللي جواه

مثلًا عندك عمود Indexability. متفترضش إن القيمة Non-Indexable. ممكن تكون Non Indexable أو Noindex أو حاجة مختلفة حسب المصدر.

اعمل:

وشوف القيم الفعلية. بعدها اكتب الشرط.

نفس الفكرة مع Status

ممكن تكتشف 200 و301 و302 و404 و403 و500. وبناء عليه تبدأ تسأل أسئلة أفضل.

بايثون ممكن تساعدك تسأل أسئلة، مش بس تجاوب عليها

دي نقطة مهمة. أحيانًا متبقاش داخل الملف وعارف المشكلة. ممكن تستخدم بايثون علشان تستكشف. مثلًا:

فتكتشف إن عندك 8,000 URL بـ 302. وده رقم ملفت. أو print(df["Indexability"].value_counts()) فتكتشف إن نصف الموقع Non-Indexable.

أو ترتب:

فتبدأ تشوف Pattern معين.

يعني بايثون مش بس: أنا عندي سؤال، هات الإجابة. ممكن تكون: ساعدني أفهم البيانات علشان أعرف أسأل السؤال الصح.

متحولش كل Audit إلى Rules جامدة

لو إنت كتبت:

ده Filter. مش قانون SEO.

لو كتبت:

دي صفحات تجاوزت Threshold إنت حددته. لكن لازم تعرف ليه إنت مختار Threshold ده، وهل فعلًا مناسب للمهمة.

الكود بينفذ قرارك. مش بيثبت إن القرار صح.

دلوقتي إزاي تبني أي SEO Script بسيط؟

استخدم الأسئلة دي بالترتيب.

1. إيه المشكلة؟

مثلًا: عايز أعرف صفحات 404. مش: عايز أستخدم Pandas. ابدأ بالمشكلة، مش بالأداة.

2. البيانات فين؟

مثلًا crawl.csv أو urls.txt.

3. محتاج أي أعمدة أو قيم؟

مثال URL وStatus.

4. إيه الشرط أو المنطق؟

مثلًا Status = 404.

5. هستخدم أنهي أداة؟

  • عندي جدول: Pandas.
  • عندي List بسيطة وعايز أمر على العناصر: Loop.
  • عندي عملية مكررة: Function.
  • عندي URL وعايز أفك أجزاؤه: urlparse.

6. إزاي هتأكد إن النتيجة صح؟

7. النتيجة هتروح فين؟

مثلًا print أو CSV أو Excel.

تعالى نطبق الأسئلة دي

المشكلة: عايز أعرف صفحات Blog اللي عدد كلماتها أقل من 100.

المشكلة؟

Blog pages under 100 words

البيانات؟

crawl.csv

الأعمدة؟

URL وStatus وWords

الشروط؟

URL contains /blog/، وStatus = 200، وWords < 100

الأداة؟

جدول. إذن Pandas مناسبة.

الكود

مثال
الناتج المتوقع
Empty DataFrame
Columns: [URL, Status, Title, Indexability, Words]
Index: []
ملحوظة
الملف اللي بنستخدمه هنا مفيهوش صفحة Blog تحت 100 كلمة فعليًا (أقل كلمة عندنا 1500)، فالنتيجة فاضية. لكن الهدف إنك تشوف الكود نفسه بيتبني إزاي من الأسئلة السبعة، مش رقم النتيجة بالذات.

التحقق

الحفظ

شايف إن الكود جه تقريبًا في آخر التفكير. وده المقصود من الدرس.

قبل المشروع النهائي

عايزك تدخل المشروع القادم بعقلية مختلفة. لما تشوف مهمة كبيرة، متقولش:

خد بالك
أنا مش عارف أكتب البرنامج ده.

قول:

ملحوظة
أول خطوة إيه؟

اعملها. بعدها: الخطوة اللي بعدها إيه؟ اعملها.

البرنامج الكبير في النهاية مجموعة خطوات صغيرة.

اختبار الدرس

الاختبار المرة دي مش هدفه تعرف اسم Function. هدفه نعرف هل بدأت تفكر بالطريقة الصح.

عندك ملف seo-audit.csv وفيه:

قبل ما تكتب أي كود، اكتب لنفسك الخطوات المطلوبة لحل كل سؤال.

السؤال الأول

عايز تعرف: كام URL موجود في الملف؟ حدد Input وProcessing وOutput، وبعدين اكتب الكود.

جاوب بأسلوبك

اكتب خطوات Input وProcessing وOutput لمعرفة عدد الروابط في seo-audit.csv، ثم اكتب الكود نفسه.

اكتب إجابتك الأول. الفايدة في إنك تحاول قبل ما تشوف.

السؤال الثاني

عايز تعرف: عندي كام صفحة لكل Status Code؟ فكر الأول: هل محتاج Loop بإيدك؟ ولا عندك أداة اتعلمناها في Pandas تعمل ده؟

جاوب بأسلوبك

اكتب الأداة اللي هتستخدمها لمعرفة عدد الصفحات لكل Status Code، ثم اكتب الكود.

اكتب إجابتك الأول. الفايدة في إنك تحاول قبل ما تشوف.

السؤال الثالث

عايز تطلع: صفحات 200 اللي Title بتاعها مفقود. قبل الكود اكتب الشرط بالعربي. المفروض النتيجة تحتوي على https://example.com/blog/python/ فقط.

جاوب بأسلوبك

اكتب الشرط بالعربي الأول (هات الصفوف اللي...)، ثم حوّله لكود Pandas.

اكتب إجابتك الأول. الفايدة في إنك تحاول قبل ما تشوف.

السؤال الرابع

عايز تطلع: صفحات Blog اللي Status بتاعها 200. خلي بالك، مش أي Blog URL. يعني https://example.com/blog/old/ لازم متظهرش لأنها 404.

جاوب بأسلوبك

اكتب الشرطين اللي محتاجهم (على الرابط، وعلى Status)، ثم اكتب الكود.

اكتب إجابتك الأول. الفايدة في إنك تحاول قبل ما تشوف.

السؤال الخامس

عايز تطلع: صفحات 200 اللي عدد الكلمات فيها أقل من 100. المفروض تظهر https://example.com/blog/python/ وhttps://example.com/category/phones/.

جاوب بأسلوبك

اكتب الكود اللي يحل السؤال ده.

اكتب إجابتك الأول. الفايدة في إنك تحاول قبل ما تشوف.

السؤال السادس

لما طلعنا الصفحات اللي عدد كلماتها أقل من 100، هل نقدر نقول: الصفحتين دول عندهم Thin Content ولازم يتصلحوا؟

جاوب بأسلوبك

جاوب: هل بايثون في الحالة دي بتاخد قرار Thin Content، ولا بتطلع مرشّحين بس؟ ليه؟

اكتب إجابتك الأول. الفايدة في إنك تحاول قبل ما تشوف.

السؤال السابع

افترض إنك كتبت:

لكن المطلوب كان: صفحات Status 200. هل الكود صحيح من ناحية بايثون؟ وهل المنطق صحيح بالنسبة للمهمة؟ وضح الفرق.

جاوب بأسلوبك

وضّح الفرق بين df[df['Status'] != 404] و df[df['Status'] == 200] بالنسبة لمهمة 'هات صفحات 200'.

اكتب إجابتك الأول. الفايدة في إنك تحاول قبل ما تشوف.

السؤال الثامن

كتبت:

وظهر KeyError: 'Page Title'. إيه أول خطوة تعملها قبل ما تبدأ تغير الكود؟

جاوب بأسلوبك

إيه أول خطوة عملية تعملها لما يظهر KeyError على اسم عمود؟

اكتب إجابتك الأول. الفايدة في إنك تحاول قبل ما تشوف.

السؤال التاسع

عندك ملف فيه 100,000 URL وإنت لسه بتجرب منطق جديد. إيه الأفضل في البداية؟ تجرب كل مرة على 100,000 URL؟ ولا تبدأ بعينة صغيرة؟ وضح ليه.

جاوب بأسلوبك

إيه الأفضل: تجرب على الملف الكامل، ولا على عينة صغيرة (df.head(100) مثلًا)؟ وضح ليه.

اكتب إجابتك الأول. الفايدة في إنك تحاول قبل ما تشوف.

السؤال العاشر

عندك الكود ده:

الكود شغال. لكن عدله بأسماء Variables أوضح تساعدك تفهم البرنامج بعد أسبوع.

جاوب بأسلوبك

أعد كتابة الكود بأسماء متغيرات أوضح من x وy.

اكتب إجابتك الأول. الفايدة في إنك تحاول قبل ما تشوف.

Final Mindset Challenge

عندك ملف Crawl كبير. المطلوب:

ملحوظة
طلع صفحات 200 القابلة للفهرسة، اللي Title بتاعها مفقود أو عدد كلماتها أقل من 100، وخلي النتيجة تحتوي فقط على URL وTitle وWords، وبعدها احفظها في ملف اسمه pages-to-review.csv.

متكتبش الكود فورًا. الأول اكتب:

خطوات التفكير
  1. 1Input
  2. 2الأعمدة المطلوبة
  3. 3الشروط
  4. 4طريقة التحقق
  5. 5Output

بعدها حوّل كل خطوة إلى بايثون.

وخلي بالك من حاجة مهمة جدًا في صياغة المشكلة. عندنا:

وداخل الصفحات دي، نريد الصفحة لو:

أو:

يعني المنطق مش كل الشروط AND. فكر كويس في الجزء ده قبل ما تكتب الكود.

تحدّي SEO

ابني تقرير مراجعة من البداية للنهاية

اقرأ seo-audit.csv، وطلّع صفحات Status = 200 وIndexability = Indexable، واللي بينهم يكون Title مفقود أو Words أقل من 100 (شرط أو مش شرط و)، وخلي النتيجة تحتوي على URL وTitle وWords بس، واحفظها في pages-to-review.csv بدون Index.

لو وصلت للحل بنفسك، فأنت جاهز للمشروع النهائي. لأن أهم حاجة قبل المشروع مش إنك تحفظ read_csv() أو isna()، لكن إنك تعرف تاخد مشكلة SEO، تفكها، تحدد البيانات والشروط، تختار الأداة، تختبر النتيجة، وبعدها فقط تعتمد الـ Output.

وده هو الفرق بين شخص بيحفظ أكواد بايثون، وشخص بدأ فعلًا يستخدم بايثون كأداة في شغل SEO.

بقيت تمارين لم تحلّها

0/12

التمارين المتبقية: 12 من 12. يُحتسب الدرس مكتملًا حين تحلّها كلها، ويمكنك العودة إليها متى شئت.

أحسنت، وصلت إلى نهاية الدرس.