كيف تفكّر ببايثون لحل مشكلة SEO؟
45 دقيقة تقريبًا
- Variable
- String
- Integer
- Float
- Boolean
- List
- Dictionary
- 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 وقلتلك:
هل أول حاجة المفروض تعملها إنك تبدأ تكتب كود؟
لا. ودي بالظبط الفكرة اللي هنشتغل عليها في الدرس ده.
المشكلة مش إنك تعرف أمر بايثون أو متعرفوش. المشكلة الأهم إنك تعرف:
وبعدها:
النهاردة مش هنتعلم مكتبة جديدة، ومش هحاول أديك عشرين أمر جديد. إحنا هنستخدم اللي اتعلمناه، لكن هنركز على طريقة التفكير قبل وأثناء كتابة الكود.
وده درس مهم جدًا قبل المشروع النهائي.
متبدأش من الكود، ابدأ من المشكلة
خلينا ناخد المثال ده:
المبتدئ غالبًا يشوف المشكلة ويبدأ يدور:
وبعدها ينسخ أي كود يلاقيه. ممكن الكود يشتغل. لكن لو تغير اسم عمود واحد، أو شكل الملف اختلف، غالبًا هيقف ومش هتبقى فاهم ليه.
إحنا عايزين طريقة تانية. قبل أي كود، اسأل نفسك:
البيانات موجودة فين؟
عندي ملف crawl.csv.
إيه المعلومات اللي محتاجها؟
محتاج URL وStatus وTitle.
إيه الشرط؟
عندي شرطين: Status = 200 وTitle مفقود.
عايز أعمل إيه بالنتيجة؟
مثلًا: أطبعها، أراجعها، أحفظها في CSV جديد.
إذن المشكلة اللي كانت شكلها كبير بدأت تتحول لحاجات صغيرة.
- 1اقرأ الملف.
- 2تأكد من الأعمدة.
- 3فلتر Status 200.
- 4فلتر Title المفقود.
- 5اعرض النتيجة.
- 6احفظها.
دلوقتي فقط نبدأ نفكر في الكود.
حول المشكلة لجمل بسيطة الأول
قبل ما تكتب:
لازم تكون قادر تقول لنفسك بالعربي:
لو الجملة نفسها مش واضحة في دماغك، الكود غالبًا هيكون أصعب.
خلي بالك من النقطة دي. بايثون مش هي اللي تحدد لك المشكلة. إنت الأول بتحدد المنطق. وبعدها بتحوله إلى بايثون.
يعني عندنا Status = 200 تتحول إلى df["Status"] == 200.
وعندنا Title مفقود تتحول إلى df["Title"].isna().
وعندنا الشرط الأول والشرط الثاني فتتحول في Pandas إلى &. فيبقى:
أنت هنا مش حفظت سطر. أنت بنيته. وده فرق كبير جدًا.
متحلش المشكلة الكبيرة مرة واحدة
خلينا نقول إن عندك المهمة دي:
لو حاولت تكتب البرنامج كله مرة واحدة، احتمال كبير جدًا تغلط ومتعرفش الغلط فين.
الأفضل تكسر المهمة. أول حاجة:
لو النتيجة ظهرت صح، نكمل. بعدها:
لو أسماء الأعمدة موجودة زي ما متوقعين، نكمل. بعدها نجرب أول شرط:
اشتغل؟ نكمل. بعدها:
اشتغل؟ نكمل. بعدها نحفظ:
إنت كده بنيت البرنامج جزء جزء. ولو حاجة غلط حصلت، هتعرف تقريبًا في أي خطوة حصلت.
دي قاعدة مهمة جدًا في التعلم
متكتبش 50 سطر كود وبعدها تشغلهم لأول مرة.
- 1اكتب جزء صغير.
- 2شغله.
- 3شوف الناتج.
- 4افهمه.
- 5وبعدين كمل.
وده سبب إننا من أول الكورس بنطبع النتائج. مش لأن print() هي الحل النهائي. لكن لأنها بتخليك تشوف بايثون بتعمل إيه.
أول Trick: افحص البيانات قبل ما تحللها
افترض إن حد بعتلك ملف crawl.csv وبدأت على طول تعمل:
لكن العمود أصلاً اسمه Status Code. الكود هيطلع Error.
أو ممكن Status يكون مكتوب فيه OK وNot Found مش أرقام.
أو ممكن الملف أصلاً مش هو الملف اللي فاكره.
علشان كده أول ما تقرأ Data Set جديدة، اعمل فحص سريع. مثلًا:
النتائج ممكن تكون:
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). هل تكمل التحليل؟
الأفضل الأول تسأل نفسك:
ممكن تكون صدرت جزء من 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 صحيح.
اسأل نفسك: أنا بفلتر على إيه؟
خلينا ناخد مثال. عندك:
| URL | Status | Indexability |
|---|---|---|
| /page-1/ | 200 | Indexable |
| /page-2/ | 200 | Non-Indexable |
| /page-3/ | 404 | Non-Indexable |
| /page-4/ | 301 | Non-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 تكون أفضل؟
لو عندك جدول:
| URL | Status | Title | Words |
|---|---|---|---|
| /page-1/ | 200 | Page 1 | 500 |
| /page-2/ | 404 | Page 2 | 0 |
| /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
لو لقيت نفسك بتنسخ نفس الكود أكتر من مرة، اسأل نفسك:
مش معنى كده إن كل 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
دي من أحسن الطرق اللي تخليك تفهم أي برنامج هنعمله بعد كده. أي مهمة تقريبًا ممكن تقسمها إلى:
- 1Input
- 2Processing
- 3Output
Input
البيانات جاية منين؟ مثلًا crawl.csv أو urls.txt أو بعد كده Website وAPI وSearch Console.
في مثالنا:
ده جزء الـ Input.
Processing
هنعمل إيه بالبيانات؟ مثلًا:
ده Processing. إحنا أخدنا البيانات وطبقنا عليها منطق.
Output
عايز النتيجة فين؟ ممكن:
أو:
ده Output.
ليه التقسيم ده مهم؟
لأن لما البرنامج يكبر، تقدر تسأل: المشكلة في Input؟ يعني الملف متقراش صح؟
ولا المشكلة في Processing؟ الشرط غلط؟
ولا المشكلة في Output؟ النتيجة صح لكن الملف متحفظش؟
بدل ما تبص على البرنامج كله ككتلة واحدة.
مثال كامل بطريقة التفكير
المهمة:
متكتبش كود الأول. نفكر.
البيانات جاية منين؟
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المشكلة
- 2المنطق
- 3الخطوات
- 4الأداة
- 5Syntax
مش: Syntax، وبعدين أشوف هعمل بيها إيه.
مثال: عندك 100,000 URL وفيه Crawl Trap محتمل
مش هندخل دلوقتي في تشخيص Crawl Trap نفسه، لكن خلينا نفكر في البيانات.
عندك CSV فيه URL وStatus، ولاحظت URLs كثيرة بالشكل:
مهمتك الأول: أعرف عدد URLs اللي فيها Parameters. نفكر.
البيانات: CSV. العمود: URL. الشرط: الرابط يحتوي على ?. إذن ممكن نستخدم:
لكن هنا ظهر عندنا شكل غريب: "\?". ومش عايز أدخلك في موضوع Regular Expressions في الدرس ده.
فممكن بدل كده نرجع للحاجة اللي إنت فاهمها بالفعل. نضيف عمود منطقي باستخدام apply()؟ برضه دي حاجة جديدة ومش محتاجينها.
إذن إيه القرار الصح حاليًا؟
ممكن نستخدم 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 إلى مرحلتين:
- 1Detection
- 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.
قبل ما نكتب الكود، هنحدد اللي عايزينه. عايزين:
- صفحات 404.
- صفحات 301.
- صفحات 200 بدون Title.
- صفحات 200 غير Indexable.
- صفحات 200 أقل من 100 كلمة.
- عدد كل 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. لو الملف فيه:
| URL | Status |
|---|---|
| /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.
الطريقة الثانية:
- 1أعرف آخر جزء اشتغل صح.
- 2أعرف أول جزء طلع غلط.
- 3أفحص القيم.
- 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: []
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 كبير. المطلوب:
متكتبش الكود فورًا. الأول اكتب:
- 1Input
- 2الأعمدة المطلوبة
- 3الشروط
- 4طريقة التحقق
- 5Output
بعدها حوّل كل خطوة إلى بايثون.
وخلي بالك من حاجة مهمة جدًا في صياغة المشكلة. عندنا:
وداخل الصفحات دي، نريد الصفحة لو:
أو:
يعني المنطق مش كل الشروط AND. فكر كويس في الجزء ده قبل ما تكتب الكود.
ابني تقرير مراجعة من البداية للنهاية
اقرأ 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. يُحتسب الدرس مكتملًا حين تحلّها كلها، ويمكنك العودة إليها متى شئت.
أحسنت، وصلت إلى نهاية الدرس.