قراءة ملفات CSV وExcel في بايثون
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
- set
- lower
- rstrip
- replace
- split
- csv
- reader
- DictReader
- writer
- DictWriter
- fieldnames
يحسب العدّاد في الأعلى تمارين الدرس، وعددها 1، أما القراءة وتشغيل الأمثلة فلا تُحتسب.
في الدرس السادس بدأنا نتعامل مع الملفات، وكان عندنا ملف بسيط بالشكل ده:
كل سطر فيه URL واحد، فالموضوع كان بسيط.
لكن في شغل SEO الحقيقي، لما تعمل Crawl لموقع أو تصدر بيانات من أداة، غالبًا مش هيكون عندك URL فقط. ممكن يكون عندك بيانات بالشكل ده:
| Address | Status Code | Title | Indexability |
|---|---|---|---|
| https://example.com/ | 200 | Home | Indexable |
| https://example.com/about/ | 200 | About Us | Indexable |
| https://example.com/old-page/ | 404 | — | Non-Indexable |
يعني كل صفحة عندها أكتر من معلومة. عندنا URL وStatus Code وTitle وIndexability.
وهنا الملف النصي العادي اللي استخدمناه في الدرس السابق مش هيكون أفضل شكل للبيانات. علشان كده هنبدأ نتعامل مع نوع من أهم الملفات اللي هتقابلك كمتخصص SEO: CSV.
وبعد ما نفهمه، هنفهم علاقته بملفات Excel.
الأول، يعني إيه CSV؟
خلينا نبدأ من الملف نفسه قبل بايثون. لو عندنا الجدول ده:
| URL | Status | Title |
|---|---|---|
| https://example.com/ | 200 | Home |
| https://example.com/about/ | 200 | About |
| https://example.com/missing/ | 404 | Missing Page |
ممكن يتحفظ في ملف اسمه crawl.csv. ولو فتحنا محتوى الملف كنص، ممكن نلاقيه بالشكل ده:
بص على أول سطر URL,Status,Title. دي أسماء الأعمدة. وبعدها https://example.com/,200,Home ده أول صف بيانات.
القيم هنا متقسمة باستخدام , يعني الفاصلة. ومن هنا جه اسم CSV، وهو اختصار Comma-Separated Values يعني قيم مفصولة بفواصل.
لكن بالنسبة لنا، الأهم من معنى الاسم إننا نفهم شكل الملف.
كل سطر يمثل صف. وكل قيمة داخل السطر تمثل عمود.
ننشئ أول ملف CSV
اعمل ملف اسمه crawl.csv وحط جواه:
دلوقتي ممكن تقول: إحنا اتعلمنا Files في الدرس السادس، هل ينفع أقرأ الملف باستخدام open()؟ ينفع. نجرب:
URL,Status,Title https://example.com/,200,Home https://example.com/about/,200,About Us https://example.com/old-page/,404,Old Page https://example.com/blog/,200,Blog
الكود اشتغل. لكن المشكلة إن بايثون قرأت الملف كله كنص.
إحنا مش عايزين URL,Status,Title كنص واحد. إحنا عايزين بايثون تفهم إن عندنا ثلاثة أعمدة: URL وStatus وTitle.
وهنا هنحتاج أداة مخصصة للتعامل مع CSV.
استخدام مكتبة csv
بايثون فيها أداة جاهزة اسمها csv. علشان نستخدمها، نكتب import csv.
إحنا قابلنا فكرة import في الدرس السابق لما استخدمنا from urllib.parse import urlparse. الفكرة نفسها. إحنا بنقول لبايثون: محتاج أدوات التعامل مع CSV في البرنامج ده.
نجرب أول قراءة:
['URL', 'Status', 'Title'] ['https://example.com/', '200', 'Home'] ['https://example.com/about/', '200', 'About Us'] ['https://example.com/old-page/', '404', 'Old Page'] ['https://example.com/blog/', '200', 'Blog']
لاحظ الفرق. قبل استخدام csv.reader() كان كل سطر مجرد نص. دلوقتي كل صف أصبح List.
أول صف ['URL', 'Status', 'Title']. الصف الثاني ['https://example.com/', '200', 'Home']. يعني بايثون بدأت تفهم إن السطر يحتوي على قيم منفصلة.
يعني إيه reader؟
عندنا السطر reader = csv.reader(file). إحنا خدنا الملف file وبعتناه إلى csv.reader()، وقلنا لها:
وبعدها حفظنا النتيجة داخل reader. ثم استخدمنا for row in reader: يعني:
أول دورة row تكون ['URL', 'Status', 'Title']. الدورة الثانية row تكون ['https://example.com/', '200', 'Home']. وهكذا.
لاحظ إننا رجعنا نستخدم حاجات اتعلمناها قبل كده: Files وLoops وLists.
الجديد فقط هو إن csv.reader() ساعدتنا نفهم شكل CSV.
إزاي نجيب قيمة معينة من الصف؟
خد الصف ده:
إحنا اتعلمنا قبل كده إن الـ List فيها Index. أول عنصر row[0]. ثاني عنصر row[1]. ثالث عنصر row[2]. نجرب:
https://example.com/ 200 Home
إذن لو بنقرأ CSV:
URL https://example.com/ https://example.com/about/ https://example.com/old-page/ https://example.com/blog/
إحنا كده طبعنا العمود الأول. ولو استخدمنا:
Status 200 200 404 200
فيه مشكلة صغيرة
أول نتيجة عندنا URL، وأول نتيجة في الـ Status Status. لكن دول مش بيانات صفحات. دول أسماء الأعمدة.
السطر الأول في CSV غالبًا بيكون Header أو رؤوس الأعمدة. يعني URL,Status,Title مش صفحة داخل الموقع.
إحنا محتاجين نتعامل مع البيانات حسب أسماء الأعمدة بدل ما نظل نقول row[0] row[1] row[2].
وهنا عندنا طريقة أفضل، وخصوصًا إننا اتعلمنا Dictionaries قبل كده.
استخدام DictReader
بدل csv.reader() هنستخدم csv.DictReader(). نجرب:
{'URL': 'https://example.com/', 'Status': '200', 'Title': 'Home'}
{'URL': 'https://example.com/about/', 'Status': '200', 'Title': 'About Us'}
{'URL': 'https://example.com/old-page/', 'Status': '404', 'Title': 'Old Page'}
{'URL': 'https://example.com/blog/', 'Status': '200', 'Title': 'Blog'}هنا الموضوع بقى أسهل بكتير. كل صف أصبح Dictionary. يعني بدل ما نقول row[0] ونحاول نفتكر العمود رقم صفر كان إيه، نقدر نقول row["URL"].
ولو عايزين Status: row["Status"]. ولو عايزين Title: row["Title"]. نجرب:
https://example.com/ https://example.com/about/ https://example.com/old-page/ https://example.com/blog/
لاحظ إن URL نفسها لم تظهر المرة دي. ليه؟ لأن DictReader فهم إن أول سطر URL,Status,Title هو أسماء الأعمدة، واستخدم الأسماء دي كمفاتيح للـ Dictionary.
نطبع أكتر من معلومة عن الصفحة
https://example.com/ 200 Home https://example.com/about/ 200 About Us https://example.com/old-page/ 404 Old Page https://example.com/blog/ 200 Blog
كده بقينا نقدر نمسك أي معلومة موجودة في ملف الـ Crawl.
أول تطبيق SEO: استخراج صفحات 404
دلوقتي عندنا crawl.csv وفيه:
عايزين نطلع صفحات 404. نعمل List فاضية:
وبعدين نفحص كل Row:
['https://example.com/old-page/']
الكود بسيط جدًا لأن كل حاجة فيه إحنا عارفينها. الجديد فقط إن البيانات جاية من CSV.
لكن ليه كتبنا "404" مش 404؟
ركز في الشرط if row["Status"] == "404": مكتبناش if row["Status"] == 404: ليه؟
تعالى نشوف بايثون قرأت الـ Status كإيه.
200 <class 'str'> 200 <class 'str'> 404 <class 'str'> 200 <class 'str'>
يعني القيم جاية كـ String حتى لو شكلها رقم. وده شيء مهم جدًا تعرفه في ملفات CSV.
CSV مش بيقول لبايثون: العمود ده Integer والعمود ده String. هو في الأساس ملف نصي. علشان كده 404 وصلت لبايثون كـ String.
لو احتجنا نتعامل معاها كرقم، إحنا نقدر نحولها باستخدام int(). مثلًا:
404 <class 'int'>
لكن في مثالنا الحالي إحنا مش محتاجين عمليات حسابية على Status Code. فممكن ببساطة نقارن row["Status"] == "404".
نزود البيانات شوية
خلينا نخلي الملف أقرب لحاجة ممكن تشوفها في Crawl. اعمل crawl.csv بالشكل ده:
دلوقتي عايزين نطلع الصفحات اللي Non-Indexable. نكتب:
['https://example.com/old-page/', 'https://example.com/private/']
بدأنا دلوقتي نستخدم CSV زي ما هنستخدمه فعلًا. بدل ملف فيه URLs فقط، عندنا Data Set فيه أكتر من معلومة عن كل URL.
ممكن نستخدم أكتر من شرط
عايزين الصفحات اللي Status = 200 وفي نفس الوقت Indexability = Non-Indexable. إحنا اتعلمنا and قبل كده. فنكتب:
['https://example.com/private/']
ليه old-page متضافتش؟ لأنها Non-Indexable صحيح. لكن Status بتاعها 404، وإحنا طالبين الشرطين مع بعض.
مشكلة مهمة جدًا: اسم العمود لازم يكون مظبوط
افترض إن الملف فيه Address,Status Code,Title 1 لكن إنت كتبت row["URL"]. بايثون مش هتفهم إن Address وURL بالنسبة لك معناهم نفس الحاجة.
اسم المفتاح لازم يكون نفس اسم العمود. خلينا نشوف أسماء الأعمدة الموجودة فعلًا. ممكن نستخدم reader.fieldnames. نجرب:
['URL', 'Status', 'Title', 'Indexability']
دي حركة مهمة جدًا لما تستلم CSV جديد. قبل ما تبدأ تكتب شروط على الأعمدة، اعرف أسماء الأعمدة الموجودة فعلًا. خصوصًا إن أسماء الأعمدة بتختلف من أداة للتانية.
نبدأ نكتب CSV جديد
إحنا لحد دلوقتي بنقرأ البيانات. لكن زي ما عملنا في ملفات TXT، عايزين نحفظ النتائج.
افترض إننا طلعنا صفحات 404:
ممكن طبعًا نحفظهم في TXT. لكن إحنا دلوقتي عايزين نعمل CSV. هنستخدم csv.writer(). نجرب أبسط مثال:
URL,Issue https://example.com/old-page/,404
هنا ظهر عندنا حاجتين جداد: csv.writer() وwriter.writerow().
الأولى بتجهز لنا أداة تكتب CSV. والثانية بتكتب Row واحد. يعني writer.writerow(["URL", "Issue"]) كتبت URL,Issue. وبعدها writer.writerow(["https://example.com/old-page/", "404"]) كتبت الصف الثاني.
طيب إيه newline=""؟
لاحظ إننا كتبنا with open("results.csv", "w", encoding="utf-8", newline="") as file:
في ملفات CSV بنستخدم غالبًا newline="" علشان بايثون ومكتبة csv يتعاملوا مع نهاية الصفوف بشكل صحيح، وخصوصًا علشان منتجنبش ظهور أسطر فارغة زيادة بين الصفوف على بعض الأنظمة.
مش محتاج تحفظ تفاصيل داخلية أكتر من كده دلوقتي. اعتبر إن الشكل اللي هنستخدمه عند كتابة CSV هو with open("results.csv", "w", encoding="utf-8", newline="") as file:
نطلع صفحات 404 ونحفظها في CSV
هنرجع للملف:
هنقرأه الأول:
[{'URL': 'https://example.com/old-page/', 'Status': '404', 'Title': 'Old Page', 'Indexability': 'Non-Indexable'}]المرة دي مخزناش URL فقط. كتبنا broken_pages.append(row) يعني حفظنا الـ Row بالكامل.
وده مفيد لأننا لما نحفظ النتيجة، ممكن نحتاج URL وStatus وTitle وIndexability مش URL بس.
استخدام DictWriter
بما إن البيانات عندنا Dictionaries، فيه أداة مناسبة جدًا اسمها csv.DictWriter(). نجرب:
URL,Status,Title,Indexability https://example.com/old-page/,404,Old Page,Non-Indexable
الكود شكله أكبر، لكن لو قسمناه هتلاقيه بسيط.
نفهم fieldnames
كتبنا fieldnames = ["URL", "Status", "Title", "Indexability"]. إحنا بنحدد الأعمدة اللي الملف الجديد هيحتوي عليها.
بعدها writer = csv.DictWriter(file, fieldnames=fieldnames) يعني: هكتب CSV باستخدام Dictionaries، ودي أسماء الأعمدة.
بعدها writer.writeheader() دي كتبت أول صف URL,Status,Title,Indexability. ثم for row in broken_pages: writer.writerow(row) كتبنا كل Row.
ليه DictReader وDictWriter مهمين بالنسبة لنا؟
تخيل عندك ملف فيه 25 عمود. لو استخدمنا row[17] وبعد أسبوع رجعت للكود، هل هتفتكر العمود رقم 17 كان إيه؟ غالبًا لا.
لكن لما تشوف row["Status"] أو row["Indexability"] أنت فاهم مباشرة إحنا بنتعامل مع إيه.
علشان كده في شغل SEO، لما CSV يحتوي على Headers واضحة، غالبًا التعامل باستخدام Dictionaries هيكون أسهل في الفهم.
طيب فين Excel من الموضوع؟
لحد دلوقتي إحنا بنتعامل مع .csv. لكن ممكن يكون عندك ملف crawl.xlsx وده ملف Excel.
وهنا لازم نفرق بين حاجتين.
ملف CSV crawl.csv في الأساس ملف نصي. علشان كده قدرنا نفتحه باستخدام open() ومكتبة csv.
لكن ملف Excel crawl.xlsx مش مجرد ملف نصي مفصول بفواصل. ممكن يحتوي على:
- أكثر من Sheet.
- تنسيق للخلايا.
- Formulas.
- أنواع بيانات مختلفة.
- خصائص خاصة بملفات Excel.
علشان كده مينفعش نعمل:
ونتوقع إننا هنقرأ Excel زي TXT أو CSV.
CSV ولا Excel؟
لو فتحت الاتنين بعينك، ممكن تشوف جدول في الحالتين. لكن من ناحية بايثون هما مش نفس نوع الملف.
ملف CSV ممكن تتخيله بالشكل ده:
أما Excel فهو Workbook ممكن يحتوي على:
وكل Sheet تحتوي على Rows وColumns. فلو صدرت بيانات من أداة SEO، لازم تكون عارف إنت معاك .csv ولا .xlsx، لأن طريقة القراءة مختلفة.
هل هنستخدم Excel مباشرة دلوقتي؟
نقدر نتعامل مع .xlsx في بايثون باستخدام مكتبات مخصصة.
لكن لو دخلنا دلوقتي في مكتبة جديدة وبدأنا نتعلم Workbook وWorksheet وCells وRows وColumns، هنبعد عن الفكرة الأساسية اللي عايزين نفهمها.
الهدف في المرحلة دي إنك تفهم البيانات الجدولية نفسها. يعني Rows وColumns وHeaders وValues.
وده فهمناه باستخدام CSV. في الدرس التالي لما ندخل على Pandas، هنعرف نقرأ CSV وExcel بطريقة أسهل بكتير، ونبدأ نحلل البيانات فعلًا.
لكن من المهم من دلوقتي إنك متعاملش .csv و.xlsx على إنهم نفس الملف لمجرد إن الاتنين ممكن يفتحوا في Excel.
ممكن أحول Excel إلى CSV؟
آه. لو عندك ملف Excel والهدف إنك تطبق درس النهاردة فقط، تقدر تحفظ أو تصدر الـ Sheet اللي محتاجها كـ CSV، وبعدها تستخدم عليها الكود اللي اتعلمناه.
مثلًا crawl.xlsx يصبح crawl.csv. وبعدها نقدر نقرأه:
الناتج يعتمد على البيانات الموجودة داخل الملف، لكن هيكون بالشكل:
مشكلة ممكن تقابلك مع ملفات حقيقية
أحيانًا تستلم CSV وتفتحه، وتلاقي أول اسم عمود فيه رمز غريب، أو بايثون تتعامل مع أول Header بشكل غير متوقع.
ده ممكن يحصل بسبب طريقة حفظ الملف وترميزه.
إحنا استخدمنا لحد دلوقتي encoding="utf-8". لكن مع بعض ملفات CSV اللي جاية من Excel أو بعض الأدوات، ممكن تحتاج encoding="utf-8-sig". مثلًا:
['URL', 'Status', 'Title']
مش مطلوب منك دلوقتي تستخدم utf-8-sig في كل الملفات. لكن عايزك تعرفها لأنك هتقابل ملفات حقيقية من أدوات مختلفة، ولو أول Header ظهر بشكل غريب، الترميز واحد من أول الأشياء اللي نبص عليها.
تطبيق SEO كامل
هنعمل دلوقتي تطبيق يجمع اللي اتعلمناه. عندنا ملف crawl.csv ومحتواه:
عايزين البرنامج يعمل حاجتين. يطلع صفحات 404. ويطلع صفحات 200 اللي مش Indexable.
404 Pages: https://example.com/old-page/ https://example.com/missing/ 200 Non-Indexable Pages: https://example.com/private/
لاحظ إننا استخدمنا if مرتين مش if وبعدها elif. لأننا بنعمل اختبارين مستقلين.
في المثال الحالي الصفحة مش هتكون 404 و200 في نفس الوقت، لكن إحنا بنبني كل مجموعة حسب شرط مستقل خاص بيها.
نحفظ النتائج
هنحفظ صفحات 404 في 404-pages.csv، وصفحات 200 غير القابلة للفهرسة في non-indexable-200.csv. الكود:
404 Pages: https://example.com/old-page/ https://example.com/missing/ 200 Non-Indexable Pages: https://example.com/private/
وفي نفس المجلد هيظهر crawl.csv وnon-indexable-200.csv و404-pages.csv.
بايثون هنا عملت حاجة أقرب جدًا لشغل SEO الحقيقي.
- 1خدت Export.
- 2قرأت صفوفه.
- 3فهمت الأعمدة.
- 4طبقت شروط.
- 5قسمت البيانات.
- 6وطلعت ملفات جديدة جاهزة للمراجعة.
خلينا نقف عند نقطة مهمة
لحد دلوقتي إحنا بنعمل حاجات بالشكل ده: for row in reader: وبعدين if row["Status"] == "404": وبعدين broken_pages.append(row).
وده ممتاز علشان نفهم بايثون نفسها. لكن تخيل إن عندك ملف فيه 100,000 Row وعايز تعمل:
- Filtering.
- Sorting.
- Grouping.
- حساب متوسطات.
- معرفة عدد كل Status Code.
- مقارنة ملفين.
- معرفة الصفحات اللي Clicks بتاعتها أكبر من رقم معين.
- دمج Crawl مع Search Console.
ممكن نعمل كل ده بالطريقة الحالية. لكن هنكتب كود كتير.
وهنا هنفهم في الدرس التالي ليه مكتبة Pandas مهمة جدًا لمتخصص SEO. مش لأنها مجرد مكتبة مشهورة. لكن لأنها هتخلينا نتعامل مع الجدول كجدول فعلًا، بدل ما نبني كل تحليل باستخدام Loops وLists بإيدنا.
لكن لو دخلنا على Pandas من غير درس النهاردة، ممكن تحفظ أوامر زي read_csv() وDataFrame من غير ما تكون فاهم أصلًا البيانات اللي بايثون بتقرأها.
دلوقتي عندك الأساس.
إيه اللي المفروض تكون فاهمه من الدرس؟
لما تشوف ملف crawl.csv تبقى فاهم إنه بيانات جدولية. عندنا Rows وعندنا Columns، وغالبًا أول Row يحتوي على Headers.
ولو استخدمنا csv.reader() كل Row هيرجع كـ List. مثلًا ['https://example.com/', '200', 'Home'].
ولو استخدمنا csv.DictReader() هيتحول إلى Dictionary بالشكل {"URL": "https://example.com/", "Status": "200", "Title": "Home"}. وده يسمح لنا نكتب row["URL"] بدل row[0].
ونقدر نكتب CSV باستخدام csv.writer() أو csv.DictWriter().
كمان لازم تكون فاهم إن CSV وExcel مش نفس نوع الملف. .csv ملف نصي يحتوي على بيانات جدولية. أما .xlsx فهو ملف Excel له بنية مختلفة، وهنتعامل معاه بشكل أسهل في المرحلة التالية باستخدام Pandas.
الاختبار التطبيقي
seo-data.csv جاهز ليك بنفس المحتوى اللي هيتقالك. الناتج مخفي لحد ما تحل.اعمل ملف اسمه seo-data.csv وحط جواه:
المطلوب منك الأول تعمل برنامج يفتح الملف باستخدام csv.DictReader() ويطبع أسماء الأعمدة الموجودة داخل الملف.
الناتج المتوقع:
بعدها اعمل ثلاث Lists:
- عايز
broken_pagesتحتوي على الصفحات اللي Status بتاعها404. - و
redirect_pagesتحتوي على الصفحات اللي Status بتاعها301. - و
non_indexable_pagesتحتوي على الصفحات الليIndexability = Non-Indexable.
بعد كده اطبع كل مجموعة. لاحظ إن بعض الصفحات ممكن تظهر في أكتر من مجموعة، وده طبيعي لأن كل مجموعة بتجاوب على سؤال مختلف.
الجزء الأخير من الاختبار، احفظ صفحات 404 في ملف 404-pages.csv. لكن متحفظش URL فقط. عايز الملف الجديد يحتوي على URL وStatus وTitle وIndexability. يعني تحتفظ بالـ Row كامل.
حلّل ملف CSV واحفظ صفحات 404 كاملة
اقرأ seo-data.csv بـ csv.DictReader()، واعمل broken_pages (Status = 404) وredirect_pages (Status = 301) وnon_indexable_pages (Indexability = Non-Indexable)، واطبع كل مجموعة، ثم احفظ broken_pages بالكامل (كل الأعمدة) في ملف اسمه 404-pages.csv.
لو قدرت تعمل الجزء ده بنفسك، فأنت بقيت فاهم الرحلة كاملة: SEO Tool ثم CSV Export ثم بايثون ثم Rows وColumns ثم Filtering ثم CSV جديد.
والدرس التاسع هنبدأ فيه Pandas، وهنا هنشوف إزاي نفس التحليل اللي كتبناه باستخدام Loops وLists ممكن يتعمل بطريقة أقوى وأسهل لما البيانات تبدأ تكبر.
بقيت تمارين لم تحلّها
0/1التمارين المتبقية: 1 من 1. يُحتسب الدرس مكتملًا حين تحلّها كلها، ويمكنك العودة إليها متى شئت.
أحسنت، وصلت إلى نهاية الدرس.