تعلّمنا في المقال السابق كيفية استخراج النص من مستندات PDF ووورد التي تُعَدّ ملفاتٍ بتنسيق ثنائي، إذ تتطلب هذه الملفات استخدام وحدات بايثون Python خاصة للوصول إلى بياناتها، بينما تُعَد ملفات CSV و JSON مجرد ملفاتٍ نصية عادية، حيث يمكنك عرضها في محرر نصوص مثل محرر النصوص Mu. تحتوي لغة بايثون مسبقًا على وحدتين خاصتين هما csv
و json
، حيث توفّر كلٌّ منهما دوالًا لمساعدتك في العمل مع تنسيقات هذه الملفات.
يرمز الاختصار CSV إلى "القيم المفصولة بفواصل Comma-separated Values"، وملفات CSV هي جداول بيانات بسيطة مُخزَّنة بوصفها ملفات نصية عادية، وتسهّل وحدة csv
في بايثون تحليل ملفات CSV. يُنطَق الاختصار JSON بالطريقة "JAY-sawn" أو "Jason"، ولكن لا يهم كيف تنطقها لأن الناس سيقولون أنك تنطقها بطريقة خاطئة في كلتا الحالتين، وهو تنسيق يخزن المعلومات بوصفها شيفرة جافاسكربت مصدرية في ملفات نصية عادية. JSON هو اختصار لترميز الكائنات باستعمال جافاسكربت JavaScript Object Notation، ولكن لا تحتاج إلى معرفة لغة البرمجة جافاسكربت لاستخدام ملفات JSON، ولكن من المفيد معرفة تنسيق ملفات JSON لأنه يُستخدَم في العديد من تطبيقات الويب.
وحدة CSV
يمثّل كل سطر في ملف CSV صفًا في جدول البيانات، حيث تفصل الفواصل بين الخلايا الموجودة في الصف، فمثلًا سيبدو جدول البيانات example.xlsx في ملف CSV كما يلي:
4/5/2015 13:34,Apples,73 4/5/2015 3:41,Cherries,85 4/6/2015 12:46,Pears,14 4/8/2015 8:59,Oranges,52 4/10/2015 2:07,Apples,152 4/10/2015 18:10,Bananas,23 4/10/2015 2:40,Strawberries,98
سنستخدم هذا الملف لأمثلة الصدفة التفاعلية Interactive Shell الموجودة في هذا المقال، حيث يمكنك تنزيله أو إدخال النص في محرّر النصوص وحفظه بالاسم example.csv.
تُعَد ملفات CSV بسيطة، ولا تحتوي على العديد من ميزات جدول بيانات إكسل Excel مثل الميزات التالية:
- ليس لديها أنواع لقيمها، فكل شيء فيها هو سلسلة نصية String.
- ليس لديها إعدادات لحجم الخط أو لونه.
- ليس لديها أوراق عمل متعددة.
- لا يمكنها تحديد عرض الخلية وارتفاعها.
- لا يمكنها أن تحتوي على خلايا مدموجة.
- لا يمكنها تضمين الصور أو المخططات.
ميزة ملفات CSV الأساسية هي البساطة، إذ تدعمها العديد من أنواع البرامج على نطاقٍ واسع، ويمكن عرضها في برامج تحرير النصوص بما في ذلك محرّر النصوص Mu، وتُعَد ملفات CSV طريقةً مباشرة لتمثيل بيانات جداول البيانات. تنسيق ملف CSV هو مجرد ملف نصي يحتوي على قيم يُفصَل بينها بفواصل.
تُعَد ملفات CSV مجرد ملفات نصية، لذا قد تقرأها بوصفها سلسلة نصية ثم تعالج تلك السلسلة باستخدام التقنيات التي تعلمتها سابقًا في مقالٍ سابق، فمثلًا يمكنك استدعاء التابع split(',')
في كل سطر من النص للحصول على القيم المفصولة بفواصل بوصفها قائمةً من السلاسل النصية، لأن كل خلية في ملف CSV مفصولة عن غيرها من الخلايا بفاصلة، ولكن لا تمثّل جميع الفواصل في ملف CSV هذه الحدود بين الخلايا، إذ تحتوي ملفات CSV أيضًا على مجموعة خاصة بها من محارف الهروب Escape Characters للسماح بتضمين الفواصل والمحارف الأخرى كجزء من القيم، حيث لا يعالج التابع split()
محارف الهروب. يجب عليك دائمًا استخدام وحدة csv
لقراءة ملفات CSV وكتابتها بسبب هذه المخاطر المحتملة.
كائنات reader
يمكنك قراءة البيانات من ملف CSV باستخدام وحدة csv
من خلال إنشاء كائن reader
الذي يتيح لك التكرار على الأسطر الموجودة في ملف CSV. أدخِل ما يلي في الصدفة التفاعلية مع وضع الملف example.csv في مجلد العمل الحالي:
➊ >>> import csv ➋ >>> exampleFile = open('example.csv') ➌ >>> exampleReader = csv.reader(exampleFile) ➍ >>> exampleData = list(exampleReader) ➎ >>> exampleData [['4/5/2015 13:34', 'Apples', '73'], ['4/5/2015 3:41', 'Cherries', '85'], ['4/6/2015 12:46', 'Pears', '14'], ['4/8/2015 8:59', 'Oranges', '52'], ['4/10/2015 2:07', 'Apples', '152'], ['4/10/2015 18:10', 'Bananas', '23'], ['4/10/2015 2:40', 'Strawberries', '98']]
تحتوي لغة بايثون على وحدة csv
، لذا يمكننا استيرادها ➊ دون الحاجة إلى تثبيتها أولًا. يمكنك قراءة ملف CSV باستخدام وحدة csv
من خلال فتحه أولًا باستخدام الدالة open()
➋ كما تفعل مع أيّ ملف نصي آخر، ولكننا لا نستدعي التابع read()
أو readlines()
لكائن File
الذي تعيده الدالة open()
، بل نمرّره إلى الدالة csv.reader()
➌، مما يؤدي إلى إعادة كائن reader
لتستخدمه. لاحظ أنك لا تمرّر السلسلة النصية التي تمثّل اسم الملف مباشرةً إلى الدالة csv.reader()
.
أكثر الطرق مباشرةً للوصول إلى القيم الموجودة في كائن reader
هي تحويله إلى قائمة بايثون عادية من خلال تمريره إلى التابع list()
➍، حيث يعيد استخدام التابع list()
لكائن reader
قائمةً من القوائم، والتي يمكنك تخزينها في متغير مثل المتغير exampleData
الذي يؤدي إدخاله في الصدفة إلى عرض قائمةٍ القوائم ➎.
أصبح لديك ملف CSV بوصفه قائمةً من القوائم، ويمكنك الآن الوصول إلى القيمة الموجودة في صف وعمود محدّد باستخدام التعبير exampleData[row][col]
، حيث يكون row
هو فهرس إحدى القوائم الموجودة في المتغير exampleData
، ويكون col
هو فهرس العنصر الذي تريده من تلك القائمة. لندخِل الآن ما يلي في الصدفة التفاعلية:
>>> exampleData[0][0] '4/5/2015 13:34' >>> exampleData[0][1] 'Apples' >>> exampleData[0][2] '73' >>> exampleData[1][1] 'Cherries' >>> exampleData[6][1] 'Strawberries'
لاحظ أن exampleData[0][0]
ينتقل إلى القائمة الأولى ويعطي السلسلة النصية الأولى، وينتقل exampleData[0][2]
إلى القائمة الأولى ويعطينا السلسلة النصية الثالثة وإلخ.
قراءة البيانات من كائنات reader في حلقة for
يجب استخدام كائن reader
في حلقة for
بالنسبة لملفات CSV الكبيرة، مما يؤدي إلى تجنّب تحميل الملف بأكمله إلى الذاكرة دفعة واحدة، إذًا لندخِل ما يلي مثلًا في الصدفة التفاعلية:
>>> import csv >>> exampleFile = open('example.csv') >>> exampleReader = csv.reader(exampleFile) >>> for row in exampleReader: print('Row #' + str(exampleReader.line_num) + ' ' + str(row)) Row #1 ['4/5/2015 13:34', 'Apples', '73'] Row #2 ['4/5/2015 3:41', 'Cherries', '85'] Row #3 ['4/6/2015 12:46', 'Pears', '14'] Row #4 ['4/8/2015 8:59', 'Oranges', '52'] Row #5 ['4/10/2015 2:07', 'Apples', '152'] Row #6 ['4/10/2015 18:10', 'Bananas', '23'] Row #7 ['4/10/2015 2:40', 'Strawberries', '98']
استوردنا وحدة csv
وأنشأنا كائن reader
من ملف CSV، ويمكنك بعد ذلك التكرار ضمن حلقة على الصفوف الموجودة في كائن reader
، حيث يمثل كلّ صف قائمةً من القيم، وتمثّل كل قيمة خلية. يطبع استدعاء الدالة print()
رقم الصف الحالي ومحتوياته، ويمكنك الحصول على رقم الصف من خلال استخدام المتغير line_num
الخاص بكائن reader
، والذي يحتوي على رقم السطر الحالي. يمكن تكرار كائن reader
مرة واحدة فقط، لذا يجب استدعاء الدالة csv.reader
لإنشاء كائن reader
وإعادة قراءة ملف CSV.
كائنات writer
يتيح كائن writer
كتابة البيانات في ملف CSV، حيث يمكنك استخدام الدالة csv.writer()
لإنشاء هذا الكائن. لندخِل الآن ما يلي في الصدفة التفاعلية:
>>> import csv ➊ >>> outputFile = open('output.csv', 'w', newline='') ➋ >>> outputWriter = csv.writer(outputFile) >>> outputWriter.writerow(['spam', 'eggs', 'meat', 'beef']) 21 >>> outputWriter.writerow(['Hello, world!', 'eggs', 'meat', 'beef']) 32 >>> outputWriter.writerow([1, 2, 3.141592, 4]) 16 >>> outputFile.close()
استدعِ أولًا الدالة open()
ومرّر لها القيمة 'w'
لفتح الملف في وضع الكتابة ➊، مما يؤدي إلى إنشاء الكائن الذي يمكنك بعد ذلك تمريره إلى الدالة csv.writer()
➋ لإنشاء كائن writer
.
يجب أيضًا في نظام ويندوز تمرير سلسلة نصية فارغة لوسيط الكلمات المفتاحية Keyword Argument الذي هو newline
للدالة open()
، وإذا نسيت ضبط الوسيط newline
، فستكون الصفوف في الملف output.csv مزدوجة المسافات كما هو موضّح في الشكل التالي:
إذا نسيت وسيط الكلمات المفتاحية newline=''
في الدالة open()
، فسيكون ملف CSV مزدوج المسافة
يأخذ التابع writerow()
الخاص بكائنات writer
وسيطًا من نوع قائمة، حيث تُوضَع كل قيمة من هذه القائمة في خليتها الخاصة في ملف CSV الناتج، ويعيد هذا التابع عدد المحارف المكتوبة في الملف لهذا الصف بما في ذلك محارف السطر الجديد.
ينتج عن الشيفرة البرمجية السابقة ملف output.csv الذي يبدو كما يلي:
spam,eggs,meat,beef "Hello, world!",eggs,meat,beef 1,2,3.141592,4
لاحظ كيف يهرّب الكائن writer
تلقائيًا الفاصلة الموجودة في القيمة 'Hello, world!'
مع علامات الاقتباس المزدوجة في ملف CSV، إذ توفّر وحدة csv
عليك الاضطرار إلى معالجة هذه الحالات الخاصة بنفسك.
وسطاء الكلمات المفتاحية delimiter و lineterminator
لنفترض أنك تريد الفصل بين الخلايا باستخدام محرف جدولة Tab بدلًا من الفاصلة وتريد أن تكون الصفوف ذات مسافات مزدوجة، لندخِل مثلًا ما يلي في الصدفة التفاعلية:
>>> import csv >>> csvFile = open('example.tsv', 'w', newline='') ➊ >>> csvWriter = csv.writer(csvFile, delimiter='\t', lineterminator='\n\n') >>> csvWriter.writerow(['apples', 'oranges', 'grapes']) 24 >>> csvWriter.writerow(['eggs', 'meat', 'beef']) 17 >>> csvWriter.writerow(['spam', 'spam', 'spam', 'spam', 'spam', 'spam']) 32 >>> csvFile.close()
يؤدي ذلك إلى تغيير محارف المحدِّد Delimiter والفاصل بين السطور Line Terminator في ملفك، فالمحدِّد هو المحرف الذي يظهر بين الخلايا في الصف، والمحدِّد الافتراضي لملف CSV هو الفاصلة، بينما يكون الفاصل بين السطور هو المحرف الذي يأتي في نهاية الصف، فالفاصل بين السطور الافتراضي هو محرف السطر الجديد. يمكنك تغيير هذه المحارف إلى قيم مختلفة باستخدام وسطاء الكلمات المفتاحية Keyword Arguments التي هي delimiter
و lineterminator
باستخدام الدالة csv.writer()
.
يؤدي تمرير الوسطاء delimiter='\t'
و lineterminator='\n\n'
➊ إلى تغيير المحرف بين الخلايا إلى محرف الجدولة والمحرف بين الصفوف إلى محرفي سطر جديد. نستدعي بعد ذلك الدالة writerow()
ثلاث مرات لتعطينا ثلاثة صفوف.
ينتج عن ذلك ملف بالاسم example.tsv يحتوي ما يلي:
apples oranges grapes eggs meat beef spam spam spam spam spam spam
فصلنا بين الخلايا بمحارف جدولة، وبالتالي سنستخدم امتداد الملف .tsv
للقيم المفصول بينها بمحارف جدولة.
كائنات DictReader و DictWriter الخاصة بملفات CSV
من الأسهل العمل مع كائنات DictReader
و DictWriter
بدلًا من كائنات reader
و writer
بالنسبة لملفات CSV التي تحتوي على صفوف الترويسات، إذ تقرأ وتكتب كائنات reader
و writer
صفوف ملف CSV باستخدام القوائم، وتطبّق كائنات DictReader
و DictWriter
الخاصة بملفات CSV الوظائف نفسها ولكن باستخدام القواميس Dictionaries، وتستخدم الصف الأول من ملف CSV بوصفها مفاتيحًا لهذه القواميس.
نزّل الملف exampleWithHeader.csv الذي هو الملف example.csv نفسه باستثناء أنه يحتوي على ترويسات الأعمدة Timestamp و Fruit و Quantity في الصف الأول. أدخِل ما يلي في الصدفة التفاعلية لقراءة هذا الملف:
>>> import csv >>> exampleFile = open('exampleWithHeader.csv') >>> exampleDictReader = csv.DictReader(exampleFile) >>> for row in exampleDictReader: ... print(row['Timestamp'], row['Fruit'], row['Quantity']) ... 4/5/2015 13:34 Apples 73 4/5/2015 3:41 Cherries 85 4/6/2015 12:46 Pears 14 4/8/2015 8:59 Oranges 52 4/10/2015 2:07 Apples 152 4/10/2015 18:10 Bananas 23 4/10/2015 2:40 Strawberries 98
يضبط الكائنُ DictReader
ضمن الحلقة الصفَّ row
على كائن القاموس مع المفاتيح المشتقة من الترويسات الموجودة في الصف الأول، ولكنه يَضبط الصف row
على الكائن OrderedDict
الذي يمكنك استخدامه بالطريقة نفسها لاستخدام القاموس، ولكننا لن نشرح الفرق بين هاتين الطريقتين في هذا المقال. يعني استخدام الكائن DictReader
أنك لا تحتاج إلى شيفرة برمجية إضافية لتخطي معلومات ترويسة الصف الأول، لأن الكائن DictReader
يفعل ذلك نيابةً عنك.
إذا حاولتَ استخدام كائنات DictReader
مع الملف example.csv الذي لا يحتوي على ترويسات أعمدة في الصف الأول، فسيستخدم كائن DictReader
مفاتيح القاموس '4/5/2015 13:34'
و 'Apples'
و '73'
، حيث يمكننا تجنب ذلك من خلال تزويد الدالة DictReader()
بوسيطٍ ثانٍ يحتوي على أسماء الترويسات التي تريدها:
>>> import csv >>> exampleFile = open('example.csv') >>> exampleDictReader = csv.DictReader(exampleFile, ['time', 'name', 'amount']) >>> for row in exampleDictReader: ... print(row['time'], row['name'], row['amount']) ... 4/5/2015 13:34 Apples 73 4/5/2015 3:41 Cherries 85 4/6/2015 12:46 Pears 14 4/8/2015 8:59 Oranges 52 4/10/2015 2:07 Apples 152 4/10/2015 18:10 Bananas 23 4/10/2015 2:40 Strawberries 98
لا يحتوي الصف الأول من الملف example.csv على أيّ نص لعناوين الأعمدة، لذا أنشأنا عناوينا الخاصة 'time'
و 'name'
و 'amount'
.
تستخدم كائنات DictWriter
أيضًا القواميس لإنشاء ملفات CSV. إذا أردتَ أن يحتوي ملفك على صف الترويسات، فاكتب هذا الصف من خلال استدعاء الدالة writeheader()
، وإلّا فتخطى استدعاء هذه الدالة لحذف صف الترويسات من الملف. اكتب بعد ذلك كل صف من صفوف الملف CSV باستخدام استدعاء التابع writerow()
مع تمرير قاموسٍ يستخدم الترويسات بوصفها مفاتيحًا ويحتوي على البيانات المراد كتابتها في الملف.
>>> import csv >>> outputFile = open('output.csv', 'w', newline='') >>> outputDictWriter = csv.DictWriter(outputFile, ['Name', 'Pet', 'Phone']) >>> outputDictWriter.writeheader() >>> outputDictWriter.writerow({'Name': 'Alice', 'Pet': 'cat', 'Phone': '555- 1234'}) 20 >>> outputDictWriter.writerow({'Name': 'Bob', 'Phone': '555-9999'}) 15 >>> outputDictWriter.writerow({'Phone': '555-5555', 'Name': 'Carol', 'Pet': 'dog'}) 20 >>> outputFile.close()
يبدو الملف output.csv الذي تنشئه الشيفرة البرمجية السابقة كما يلي:
Name,Pet,Phone Alice,cat,555-1234 Bob,,555-9999 Carol,dog,555-5555
لاحظ أن ترتيب أزواج القيمة-المفتاح key-value في القواميس التي مرّرتها إلى الدالة writerow()
غير مهم، فهي مكتوبة بترتيب المفاتيح المعطاة إلى الدالة DictWriter()
، فمثلًا لا يزال رقم الهاتف يظهر في آخر الخرج بالرغم من أنك مرّرتَ المفتاح Phone
وقيمته قبل مفاتيح وقيم Name
و Pet
في الصف الرابع. لاحظ أيضًا أن أيّ مفاتيح مفقودة مثل 'Pet'
في {'Name': 'Bob', 'Phone': '555-9999'}
ستكون ببساطة فارغة في ملف CSV.
تطبيق عملي: إزالة الترويسة من ملفات CSV
لنفترض أن لديك مهمة مملة تتمثل في إزالة السطر الأول من عدة مئات من ملفات CSV، إذ قد تدخل هذه الملفات في عملية آلية تتطلب البيانات فقط وليس الترويسات الموجودة في أعلى الأعمدة، حيث يمكنك فتح كل ملف في إكسل وحذف الصف الأول، ثم تعيد حفظ الملف، ولكن قد يستغرق ذلك ساعات، إذًا لنكتب برنامجًا ينفّذ هذه المهمة بدلًا من ذلك.
يجب أن يفتح البرنامج كل ملفٍ امتداده .csv
في مجلد العمل الحالي، ويقرأ محتويات ملف CSV، ثم يعيد كتابة المحتويات بدون الصف الأول في ملف يحمل الاسم نفسه، مما يؤدي إلى وضع المحتويات الجديدة لملف CSV بدون الترويسات مكان المحتويات القديمة.
ملاحظة: تأكد من إنشاء نسخة احتياطية للملفات أولًا عندما تكتب برنامجًا يعدّل الملفات، فأنت لا تريد مسح ملفاتك الأصلية عن طريق الخطأ في حالة عدم عمل البرنامج بالطريقة التي تتوقعها.
إليك الخطوات العامة التي سيطبّقها برنامجك:
- البحث عن جميع ملفات CSV في مجلد العمل الحالي.
- قراءة المحتويات الكاملة لكل ملف.
- كتابة المحتويات مع تخطي السطر الأول في ملف CSV جديد.
ولكن سيحتاج برنامجك إلى تطبيق الخطوات التالية من ناحية الشيفرة البرمجية:
-
المرور على قائمة الملفات باستخدام التابع
os.listdir()
مع تخطي الملفات التي ليست ملفات CSV. -
إنشاء كائن
reader
الخاص بوحدة CSV وقراءة محتويات الملف باستخدام السمة Attribute التي هيline_num
لمعرفة السطر الذي يجب تخطيه. -
إنشاء كائن
writer
الخاص بوحدة CSV وكتابة بيانات القراءة في الملف الجديد.
افتح نافذة محرّر جديدة لإنشاء ملف جديد واحفظه بالاسم removeCsvHeader.py لهذا المشروع.
الخطوة الأولى: المرور على جميع ملفات CSV
أول شيء يجب على برنامجك فعله هو المرور على قائمة جميع أسماء ملفات CSV الموجودة في مجلد العمل الحالي، لذا اجعل الملف removeCsvHeader.py يبدو كما يلي:
#! python3 # removeCsvHeader.py - إزالة الترويسات من جميع ملفات CSV الموجودة في مجلد العمل الحالي import csv, os os.makedirs('headerRemoved', exist_ok=True) # المرور ضمن حلقة على جميع الملفات الموجودة في مجلد العمل الحالي for csvFilename in os.listdir('.'): if not csvFilename.endswith('.csv'): ➊ continue # تخطي الملفات التي ليست ملفات CSV print('Removing header from ' + csvFilename + '...') # قراءة ملف CSV مع تخطي الصف الأول # كتابة ملف CSV
يؤدي استدعاء التابع os.makedirs()
إلى إنشاء المجلد headerRemoved
الذي سنكتب فيه جميع ملفات CSV التي ليس لها ترويسات. ستقودك حلقة for
التي نكررها على التابع os.listdir('.')
إلى هذه النتيجة جزئيًا، ولكنها ستتكرر على جميع الملفات الموجودة في مجلد العمل، لذلك يجب إضافة بعض الشيفرة البرمجية في بداية الحلقة التي تتخطى أسماء الملفات التي لا تنتهي بالامتداد .csv
. تجعل التعليمة continue
➊ حلقة for تنتقل إلى اسم الملف التالي عندما تصل إلى ملف ليس ملف CSV.
اطبع رسالةً توضّح ملف CSV الذي يعمل عليه البرنامج حتى يكون هناك بعض المخرجات أثناء تنفيذ البرنامج، ثم أضف بعض التعليقات لما يجب أن يفعله باقي البرنامج.
الخطوة الثانية: قراءة ملف CSV
لا يزيل البرنامج السطر الأول من ملف CSV، بل ينشئ نسخةً جديدة من ملف CSV بدون السطر الأول، وستحل النسخة محل الملف الأصلي لأن اسم ملف النسخة هو اسم الملف الأصلي نفسه.
سيحتاج برنامجك إلى طريقة لتعقّب المرور على الصف الأول حاليًا، لذا أضِف ما يلي إلى الملف removeCsvHeader.py:
#! python3 # removeCsvHeader.py - إزالة الترويسات من جميع ملفات CSV الموجودة في مجلد العمل الحالي --snip-- # قراءة ملف CSV مع تخطي الصف الأول csvRows = [] csvFileObj = open(csvFilename) readerObj = csv.reader(csvFileObj) for row in readerObj: if readerObj.line_num == 1: continue # تخطي الصف الأول csvRows.append(row) csvFileObj.close() # كتابة ملف CSV
يمكن استخدام السمة line_num
الخاصة بكائن reader
لتحديد السطر الموجود في ملف CSV الذي يقرأه حاليًا، حيث توجد حلقة for
أخرى للمرور على الصفوف التي يعيدها كائن reader
الخاص بوحدة CSV، وستُلحَق جميع الصفوف باستثناء الصف الأول بالقائمة csvRows
.
تتحقق الشيفرة البرمجية السابقة مما إذا كان readerObj.line_num
مضبوطًا على القيمة 1 أثناء تكرار حلقة for
على كل صف. إذا كان ذلك صحيحًا، فستُنفَّذ تعليمة continue
للانتقال إلى الصف التالي دون إلحاقه بالقائمة csvRows
، وستكون قيمة الشرط False
دائمًا بالنسبة لكل صفٍ لاحق، وسيُلحَق هذا الصف بالقائمة csvRows
.
الخطوة الثالثة: كتابة ملف CSV بدون الصف الأول
أصبحت القائمة csvRows
تحتوي على كافة الصفوف باستثناء الصف الأول، ويجب الآن كتابة هذه القائمة في ملف CSV الموجود في المجلد headerRemoved. أضِف ما يلي إلى الملف removeCsvHeader.py:
#! python3 # removeCsvHeader.py - إزالة الترويسات من جميع ملفات CSV الموجودة في مجلد العمل الحالي --snip-- # المرور ضمن حلقة على جميع الملفات الموجودة في مجلد العمل الحالي ➊ for csvFilename in os.listdir('.'): if not csvFilename.endswith('.csv'): continue # تخطي الملفات التي ليست ملفات CSV --snip-- # كتابة ملف CSV csvFileObj = open(os.path.join('headerRemoved', csvFilename), 'w', newline='') csvWriter = csv.writer(csvFileObj) for row in csvRows: csvWriter.writerow(row) csvFileObj.close()
يكتب الكائن writer
الخاص بوحدة CSV القائمة الناتجة في ملف CSV موجود ضمن المجلد headerRemoved
باستخدام المتغير csvFilename
الذي استخدمناه أيضًا في كائن reader
الخاص بوحدة CSV، مما يؤدي إلى الكتابة فوق الملف الأصلي. نمر بعد إنشاء الكائن writer
على القوائم الفرعية المُخزَّنة في القائمة csvRows
ونكتب كل قائمة فرعية في الملف. تنتقل حلقة for
الخارجية ➊ إلى اسم الملف التالي من os.listdir('.')
بعد تنفيذ الشيفرة البرمجية، وسيكتمل البرنامج عند الانتهاء من تلك الحلقة.
اختبر برنامجك من خلال تنزيل الملف المضغوط removeCsvHeader.zip وفك ضغطه في مجلدٍ ما، ثم شغّل البرنامج removeCsvHeader.py في هذا المجلد، وسيكون الخرج كما يلي:
Removing header from NAICS_data_1048.csv... Removing header from NAICS_data_1218.csv... --snip-- Removing header from NAICS_data_9834.csv... Removing header from NAICS_data_9986.csv…
يجب أن يطبع هذا البرنامج اسم ملفٍ في كل مرة يزيل فيها السطر الأول من ملف CSV.
أفكار لبرامج مماثلة
تشبه البرامجُ التي يمكنك كتابتها لملفات CSV أنواعَ البرامج التي يمكنك كتابتها لملفات إكسل، لأنهما ملفات جداول بيانات، حيث يمكنك كتابة برامج تنفّذ ما يلي:
- مقارنة البيانات بين صفوف مختلفة في ملف CSV واحد أو بين ملفات CSV متعددة.
- نسخ بيانات محددة من ملف CSV إلى ملف إكسل أو العكس.
- التحقق من وجود بيانات غير صالحة أو أخطاء في تنسيق ملفات CSV وتنبيه المستخدم بهذه الأخطاء.
- قراءة البيانات من ملف CSV بوصفها دخلًا لبرامج بايثون الخاصة بك.
JSON وواجهات برمجة التطبيقات API
يُعَد ترميز الكائنات باستعمال جافاسكربت JavaScript Object Notation -أو JSON اختصارًا- طريقةً شائعة لتنسيق البيانات بوصفها سلسلة نصية واحدة يمكن أن يقرأها البشر، وهو الطريقة الأصيلة التي تكتب بها برامج جافاسكربت هياكلَ البيانات الخاصة بها وتشبه ما ستنتجه دالة pprint()
في بايثون. لست بحاجةٍ لمعرفة لغة جافاسكربت لتتمكّن من التعامل مع البيانات المكتوبة بتنسيق JSON.
إليك مثال للبيانات المكتوبة بتنسيق JSON:
{"name": "Zophie", "isCat": true, "miceCaught": 0, "napsTaken": 37.5, "felineIQ": null}
تُعَد معرفة JSON أمرًا مفيدًا، لأن العديد من مواقع الويب تقدم محتوى JSON بوصفه وسيلةً للبرامج للتفاعل مع موقع الويب، ويُعرَف ذلك بتوفير واجهة برمجة تطبيقات Application Programming Interface -أو API اختصارًا. يشبه الوصول إلى واجهة برمجة التطبيقات الوصولَ إلى أيّ صفحة ويب أخرى باستخدام عنوان URL، ولكن الفرق بينهما هو أن البيانات التي تعيدها واجهة برمجة التطبيقات تكون منسَّقة لتفهمها الأجهزة باستخدام JSON مثلًا، إذ ليس من السهل أن يقرأ البشر واجهات برمجة التطبيقات.
تتيح العديد من مواقع الويب بياناتها بتنسيق JSON، حيث توفر فيسبوك Facebook وتويتر Twitter وياهو Yahoo وجوجل Google وتمبلر Tumblr وويكيبيديا Wikipedia وفليكر Flickr و Data.gov وريديت Reddit و IMDb وروتن توميتوز Rotten Tomatoes ولينكد إن LinkedIn والعديد من المواقع الشهيرة الأخرى واجهات برمجة تطبيقات لتستخدمها البرامج. تتطلب بعض هذه المواقع التسجيل الذي يكون مجانيًا دائمًا، ويجب أن تعثر على توثيق عناوين URL التي يحتاج برنامجك إلى طلبها للحصول على البيانات التي تريدها، بالإضافة إلى التنسيق العام لهياكل بيانات JSON المُعادة. يجب توفير هذا التوثيق من خلال أيّ موقع يقدم واجهة برمجة التطبيقات، حيث إذا توافرت صفحة للمطورين "Developers"، فابحث عن التوثيق هناك.
يمكنك كتابة البرامج التي تنفّذ ما يلي باستخدام واجهات برمجة التطبيقات:
- استخلاص البيانات الخام من المواقع، حيث يكون الوصول إلى واجهات برمجة التطبيقات أسهل من تنزيل صفحات الويب وتحليل شيفرة HTML باستخدام مكتبة Beautiful Soup.
- تنزيل المنشورات الجديدة تلقائيًا من أحد حساباتك على شبكة التواصل الاجتماعي ونشرها على حسابٍ آخر، فمثلًا يمكنك أخذ منشوراتك على تمبلر ونشرها على فيسبوك.
- إنشاء "موسوعة أفلام" لمجموعة أفلامك الشخصية من خلال سحب البيانات من IMDb و Rotten Tomatoes وويكيبيديا ووضعها في ملف نصي واحد على حاسوبك.
ملاحظة: يمكنك رؤية بعض الأمثلة على واجهات برمجة تطبيقات JSON في الموارد الموجودة على موقع nostarch.
لا تعد JSON الطريقة الوحيدة لتنسيق البيانات في سلسلة نصية يمكن أن يقرأها البشر، إذ توجد العديد من اللغات الأخرى بما في ذلك لغات XML (لغة التوصيف الموسَّعة eXtensible Markup Language) و TOML (أو Tom’s Obvious, Minimal Language) و YML (أو Yet another Markup Language) و INI (أو Initialization) وتنسيقات ASN.1 القديمة (Abstract Syntax Notation One)، حيث توفر جميع هذه اللغات هيكلًا لتمثيل البيانات بوصفها نصًا يمكن أن يقرأه البشر. لن نغطّي هذه اللغات في هذا المقال، لأن تنسيق JSON أصبح التنسيق البديل الأكثر استخدامًا على نطاق واسع، ولكن توجد وحدات بايثون خارجية يمكنها التعامل معها بسهولة.
وحدة json
تتعامل وحدة json
في بايثون مع جميع تفاصيل الترجمة بين سلسلة نصية تحتوي على بيانات JSON وقيم بايثون الخاصة بالدوال json.loads()
و json.dumps()
. لا يمكن لتنسيق JSON تخزين كل أنواع قيم بايثون، إذ يمكن أن يحتوي على قيم لأنواع بياناتٍ مُحدَّدة فقط وهي: السلاسل النصية Strings والأعداد الصحيحة Integers والأعداد العشرية Floats والقيم المنطقية Booleans والقوائم Lists والقواميس Dictionaries والنوع NoneType
. كما لا يمكن لتنسيق JSON أن يمثل كائنات خاصة بلغة بايثون مثل كائنات File
أو كائنات reader
أو writer
الخاصة بوحدة CSV أو كائنات Regex
أو كائنات WebElement
الخاصة بالوحدة Selenium.
قراءة بيانات JSON باستخدام الدالة loads()
يمكنك ترجمة سلسلة نصية تحتوي على بيانات JSON إلى قيمة في لغة بايثون من خلال تمريرها إلى الدالة json.loads()
، حيث يعني اسم هذه الدالة loads تحميل سلسلة نصية "load string" ولا يعني مجموعة التحميلات "loads". لندخِل الآن ما يلي في الصدفة التفاعلية Interactive Shell:
>>> stringOfJsonData = '{"name": "Zophie", "isCat": true, "miceCaught": 0, "felineIQ": null}' >>> import json >>> jsonDataAsPythonValue = json.loads(stringOfJsonData) >>> jsonDataAsPythonValue {'isCat': True, 'miceCaught': 0, 'name': 'Zophie', 'felineIQ': None}
نستورد أولًا الوحدة json
، ثم يمكننا استدعاء الدالة loads()
وتمرير سلسلة نصية من بيانات JSON إليها، حيث تستخدم سلاسل JSON النصية دائمًا علامات اقتباس مزدوجة، وتعيد هذه الدالة البيانات بوصفها قاموس بايثون. تُعَد قواميس بايثون غير مرتبة، لذا يمكن أن تظهر أزواج مفتاح-قيمة بترتيب مختلف عند طباعة jsonDataAsPythonValue
.
كتابة بيانات JSON باستخدام الدالة dumps()
تترجم الدالة json.dumps()
قيمة بايثون إلى سلسلة نصية من البيانات بتنسيق JSON، حيث يعني اسم هذه الدالة dumps تفريغ سلسلة نصية "dump string" ولا يعني الجمع "dumps"). لندخِل الآن ما يلي في الصدفة التفاعلية:
>>> pythonValue = {'isCat': True, 'miceCaught': 0, 'name': 'Zophie', 'felineIQ': None} >>> import json >>> stringOfJsonData = json.dumps(pythonValue) >>> stringOfJsonData '{"isCat": true, "felineIQ": null, "miceCaught": 0, "name": "Zophie" }'
يمكن أن يكون نوع القيمة أحد أنواع بيانات بايثون الأساسية فقط وهي: قاموس أو قائمة أو عدد صحيح أو عدد عشري أو سلسلة نصية أو قيمة منطقية أو None
.
تطبيق عملي: جلب بيانات الطقس الحالية
يبدو التحقق من الطقس أمرًا بسيطًا إلى حد ما، حيث يمكنك فتح متصفح الويب الخاص بك والنقر على شريط العناوين، ثم كتابة عنوان URL لموقع ويب خاص بالطقس أو البحث عن موقع ثم النقر على الرابط، وانتظار تحميل الصفحة، والاطلاع على جميع الإعلانات وإلخ. هناك الكثير من الخطوات المملة التي يمكنك تخطيها إذا كان لديك برنامج ينزّل توقعات الطقس للأيام القليلة القادمة ويطبعها ضمن نصٍ عادي، حيث يستخدم هذا البرنامج الوحدة requests
لتنزيل البيانات من الويب، فالخطوات العامة التي يطبّقها هذا البرنامج هي ما يلي:
- قراءة الموقع المطلوب من سطر الأوامر.
- تنزيل بيانات JSON الخاصة بالطقس من الموقع OpenWeatherMap.org.
- تحويل سلسلة بيانات JSON إلى هيكل بيانات بايثون.
- طباعة حالة الطقس لهذا اليوم واليومين القادمين.
لذا ستطبّق الشيفرة البرمجية الخطوات التالية:
-
ضم السلاسل النصية إلى القائمة
sys.argv
للحصول على الموقع. -
استدعاء الدالة
requests.get()
لتنزيل بيانات الطقس. -
استدعاء الدالة
json.loads()
لتحويل بيانات JSON إلى هيكل بيانات بايثون. - طباعة توقعات الطقس.
افتح نافذة محرّر جديدة لإنشاء ملف جديد لهذا المشروع واحفظه بالاسم getOpenWeather.py، ثم انتقل إلى الموقع OpenWeatherMap في متصفحك وسجّل فيه على حساب مجاني للحصول على مفتاح API، ويُسمَّى أيضًا معرّف التطبيق app ID، والذي يمثل رمز سلسلة نصية يبدو مثل الرمز '30144aba38018987d84710d0e319281e'
بالنسبة لخدمة OpenWeatherMap. لا حاجة للدفع مقابل هذه الخدمة إلّا إذا أردتَ إجراء أكثر من 60 استدعاء لواجهة برمجة التطبيقات في الدقيقة. حافظ على سرية مفتاح API، إذ يمكن لأيّ شخص يعرفه كتابة سكربتات تأخذ من حصة الاستخدام الخاصة بحسابك.
الخطوة الأولى: الحصول على الموقع من وسيط سطر الأوامر
يأتي دخل هذا البرنامج من سطر الأوامر، لذا اجعل برنامج getOpenWeather.py كما يلي:
#! python3 # getOpenWeather.py - طباعة الطقس لموقعٍ ما من سطر الأوامر APPID = 'YOUR_APPID_HERE' import json, requests, sys # حساب الموقع من وسطاء سطر الأوامر if len(sys.argv) < 2: print('Usage: getOpenWeather.py city_name, 2-letter_country_code') sys.exit() location = ' '.join(sys.argv[1:]) # تنزيل بيانات JSON من واجهة برمجة تطبيقات OpenWeatherMap.org # تحميل بيانات JSON في متغير بايثون
تُخزَّن وسطاء سطر الأوامر ضمن القائمة sys.argv
في لغة بايثون، ويجب ضبط المتغير APPID
على قيمة مفتاح API الخاص بحسابك، إذ ستفشل طلباتك لخدمة الطقس بدون هذا المفتاح، ثم سيتحقق البرنامج من وجود أكثر من وسيط سطر أوامر بعد سطر Shebang (الذي يبدأ بالرمز !#) والتعليمة import
. تذكّر أن القائمة sys.argv
تحتوي دائمًا على عنصر واحد على الأقل sys.argv[0]
، والذي يحتوي على اسم ملف سكربت بايثون. إذا كان هناك عنصر واحد فقط في القائمة، فهذا يعني أن المستخدم لم يقدّم موقعًا في سطر الأوامر، وستُعرَض رسالة الاستخدام "usage" للمستخدم قبل انتهاء البرنامج.
تتطلب خدمة OpenWeatherMap تنسيق الاستعلام بالشكل: اسم المدينة ثم فاصلة ثم رمز البلد المكون من حرفين مثل الرمز "US" للولايات المتحدة الأمريكية، حيث يمكنك العثور على قائمة بهذه الرموز على ويكيبيديا. يعرض هذا السكربت الطقس للمدينة الأولى المُدرَجة في نص JSON المُعاد، ولكن ستُضمَّن جميع المدن التي لها الاسم نفسه مثل مدينة بورتلاند Portland في ولاية أوريجون ومدينة بورتلاند بولاية ماين، بالرغم من أن نص JSON سيتضمّن معلومات خطوط الطول والعرض للتمييز بين هذه المدن.
تُقسَم وسطاء سطر الأوامر بناءً على الفراغات، حيث سيجعل وسيط سطر الأوامر San Francisco, US
القائمة sys.argv
تحتوي على ['getOpenWeather.py', 'San', 'Francisco,', 'US']
، لذلك استدعِ التابع join()
لضم جميع السلاسل النصية باستثناء السلسلة النصية الأولى في القائمة sys.argv
، وخزّن هذه السلسلة النصية الناتجة عن الضم في متغير اسمه location
.
الخطوة الثانية: تنزيل بيانات JSON
يوفّر موقع OpenWeatherMap.org معلومات الطقس بتنسيق JSON في الزمن الحقيقي، ولكن يجب عليك أولًا التسجيل في الموقع للحصول على مفتاح API مجاني، حيث يُستخدَم هذا المفتاح لتقييد عدد مرات تقديم الطلبات على الخادم، مما يؤدي إلى إبقاء تكاليف حيز النطاق التراسلي منخفضة. يجب أن ينزّل برنامجك الصفحة الموجودة على الرابط:
https://api.openweathermap.org/data/2.5/forecast/daily?q=<Location>&cnt=3&APPID=<APIkey>
حيث <Location>
هو اسم المدينة التي تريد الطقس فيها و <API key>
هو مفتاح API الشخصي الخاص بك. أضِف ما يلي إلى برنامج getOpenWeather.py:
#! python3 # getOpenWeather.py - طباعة الطقس لموقعٍ ما من سطر الأوامر --snip-- # تنزيل بيانات JSON من واجهة برمجة تطبيقات OpenWeatherMap.org url ='https://api.openweathermap.org/data/2.5/forecast/daily?q=%s&cnt=3&APPID=%s ' % (location, APPID) response = requests.get(url) response.raise_for_status() # ألغِ التعليق لرؤية نص JSON الخام: #print(response.text) # تحميل بيانات JSON في متغير بايثون
يأتي الموقع location
من وسطاء سطر الأوامر، ويمكننا إنشاء عنوان URL الذي نريد الوصول إليه من خلال استخدام العنصر البديل s%
وإدراج أيّ سلسلة نصية مُخزَّنة في location
في ذلك المكان من السلسلة النصية التي تمثّل عنوان URL. نخزّن النتيجة في المتغير url
الذي نمرّره إلى الدالة requests.get()
، حيث يعيد استدعاء الدالة requests.get()
الكائن Response
، والذي يمكنك التحقق من وجود أخطاء فيه من خلال استدعاء الدالة raise_for_status()
. إن لم يظهر أيّ استثناء، فسيكون النص المُنزَّل موجودًا في response.text
.
الخطوة الثالثة: تحميل بيانات JSON وطباعة الطقس
يحتوي المتغير العضو response.text
على سلسلة نصية كبيرة من البيانات المُنسَّقة بتنسيق JSON، حيث يمكنك تحويلها إلى قيمة بايثون من خلال استدعاء الدالة json.loads()
، وستبدو بيانات JSON كما يلي:
{'city': {'coord': {'lat': 37.7771, 'lon': -122.42}, 'country': 'United States of America', 'id': '5391959', 'name': 'San Francisco', 'population': 0}, 'cnt': 3, 'cod': '200', 'list': [{'clouds': 0, 'deg': 233, 'dt': 1402344000, 'humidity': 58, 'pressure': 1012.23, 'speed': 1.96, 'temp': {'day': 302.29, 'eve': 296.46, 'max': 302.29, 'min': 289.77, 'morn': 294.59, 'night': 289.77}, 'weather': [{'description': 'sky is clear', 'icon': '01d', --snip–
يمكنك رؤية هذه البيانات من خلال تمرير المتغير weatherData
إلى الدالة pprint.pprint()
، وقد ترغب في الاطلاع على موقع openweathermap.org لمزيد من التوثيق الذي يوضّح معنى هذه الحقول، فمثلًا سيخبرك التوثيق عبر الإنترنت أن القيمة 302.29
بعد 'day'
هي درجة الحرارة أثناء النهار بواحدة الكلفن وليست بواحدة فهرنهايت أو الدرجة المئوية.
يوجد وصف الطقس الذي تريده بعد 'main'
و 'description'
، لذا أضِف ما يلي إلى برنامج getOpenWeather.py لطباعته بدقة:
! python3 # getOpenWeather.py - طباعة الطقس لموقعٍ ما من سطر الأوامر --snip-- # تحميل بيانات JSON في متغير بايثون weatherData = json.loads(response.text) # طباعة وصف الطقس ➊ w = weatherData['list'] print('Current weather in %s:' % (location)) print(w[0]['weather'][0]['main'], '-', w[0]['weather'][0]['description']) print() print('Tomorrow:') print(w[1]['weather'][0]['main'], '-', w[1]['weather'][0]['description']) print() print('Day after tomorrow:') print(w[2]['weather'][0]['main'], '-', w[2]['weather'][0]['description'])
لاحظ كيف تخزّن الشيفرة البرمجية السابقة المتغير weatherData['list']
في المتغير w
ليوفر عليك بعض الجهد عند الكتابة ➊، حيث يمكنك استخدام w[0]
و w[1]
و w[2]
لاسترداد القواميس الخاصة بطقس اليوم والغد وبعد الغد على التوالي، ويحتوي كلّ قاموس من هذه القواميس على مفتاح 'weather'
الذي يحتوي على قيمة من النوع قائمة، ويهمنا منها عنصر القائمة الأول عند الفهرس 0، وهو قاموس متداخل يحتوي على عدة مفاتيح أخرى. نطبع القيم المخزنة في المفتاحين 'main'
و 'description'
، حيث نفصل بينها بشرطة واصلة.
سيبدو الخرج كما يلي عند تشغيل هذا البرنامج باستخدام وسيط سطر الأوامر getOpenWeather.py San Francisco, CA
:
Current weather in San Francisco, CA: Clear - sky is clear Tomorrow: Clouds - few clouds Day after tomorrow: Clear - sky is clear
أفكار لبرامج مماثلة
يمكن أن يشكّل الوصول إلى بيانات الطقس الأساس الذي نبني عليه العديد من أنواع البرامج الأخرى، حيث يمكنك إنشاء برامج مماثلة لتنفيذ ما يلي:
- جمع تنبؤات الطقس للعديد من مواقع التخييم أو مسارات المشي لمسافات طويلة لمعرفة أيّ منها سيكون فيها الطقس الأفضل.
- جدولة برنامج للتحقق من الطقس بانتظام وإرسال تنبيه عند حدوث الصقيع إذا أردتَ نقل نباتاتك إلى الداخل، حيث سنوضّح لاحقًا الجدولة وكيفية إرسال البريد الإلكتروني.
- سحب بيانات الطقس من مواقع متعددة لإظهارها دفعة واحدة، أو حساب وإظهار متوسط توقعات الطقس المتعددة.
مشروع للتدريب: محوّل ملف إكسل إلى ملف CSV
يمكن لإكسل حفظ جدول بيانات في ملف CSV باستخدام بضع نقرات بالماوس، ولكن إذا أردتَ تحويل مئاتٍ من ملفات إكسل إلى ملفات CSV، فسيستغرق الأمر ساعات من النقر، لذا استخدم وحدة openpyxl
لكتابة برنامجٍ يقرأ جميع ملفات إكسل الموجودة في مجلد العمل الحالي ويخرجها بوصفها ملفات CSV.
قد يحتوي ملف إكسل واحد على أوراق متعددة، لذا يجب إنشاء ملف CSV واحد لكل ورقة، ويجب أن تكون أسماء ملفات CSV هي <excel filename>_<sheet title>.csv
، حيث يكون <excel filename>
هو اسم ملف إكسل بدون امتداد الملف مثل 'spam_data'
وليس 'spam_data.xlsx'
ويكون <sheet title>
هو السلسلة النصية التي تأتي من المتغير title
الخاص بكائن Worksheet
.
سيتضمن هذا البرنامج العديد من حلقات for
المتداخلة، وسيبدو هيكل هذا البرنامج كما يلي:
for excelFile in os.listdir('.'): # تخطي الملفات التي ليست ملفات xlsx، وتحميل كائن المصنف workbook for sheetName in wb.get_sheet_names(): # المرور ضمن حلقة على كل ورقة في المصنف sheet = wb.get_sheet_by_name(sheetName) # إنشاء اسم ملف CSV من اسم ملف إكسل وعنوان الورقة # إنشاء كائن csv.writer لملف CSV # المرور ضمن حلقة على كل صف في الورقة for rowNum in range(1, sheet.max_row + 1): rowData = [] # إلحاق كل خلية بهذه القائمة # المرور ضمن حلقة على كل خلية في الصف for colNum in range(1, sheet.max_column + 1): # إلحاق بيانات كل خلية بالقائمة rowData # كتابة القائمة rowData في ملف CSV csvFile.close()
نزّل الملف المضغوط excelSpreadsheets.zip وفك ضغط جداول البيانات في المجلد نفسه الموجود فيه برنامجك، حيث يمكنك استخدام جداول البيانات هذه كملفات لاختبار البرنامج عليها.
الخلاصة
تُعد CSV و JSON من تنسيقات النصوص العادية الشائعة لتخزين البيانات، حيث يسهُل على البرامج تحليلها مع بقاء قابليتها للقراءة، لذا تُستخدَم لجداول البيانات أو بيانات تطبيقات الويب البسيطة. تبسّط وحدتا csv
و json
عملية القراءة والكتابة في ملفات CSV و JSON بصورة كبيرة.
تعلّمنا سابقًا كيفية استخدام بايثون لتحليل معلومات مجموعة واسعة من تنسيقات الملفات، فإحدى المهام الشائعة هي أخذ البيانات من مجموعة متنوعة من التنسيقات وتحليلها للحصول على المعلومات المُحدَّدة التي تحتاجها، وتكون هذه المهام مُحدَّدة لدرجة أن البرمجيات التجارية لن تفيدك في إنجازها على النحو الأمثل، لذا يمكنك جعل حاسوبك يتعامل مع كميات كبيرة من البيانات المُقدَّمة بهذه التنسيقات من خلال كتابة السكربتات الخاصة بك.
سنبتعد في المقالات اللاحقة عن تنسيقات البيانات وسنتعلّم كيفية جعل برامجك يتواصل معك من خلال إرسال رسائل البريد الإلكتروني والرسائل النصية.
ترجمة -وبتصرُّف- للمقال Working with CSV files and JSON data لصاحبه Al Sweigart.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.