CSV פשוט יותר ממה שנראה (ומסובך יותר ממה שהייתם מצפים)
קובץ CSV הוא פשוט טקסט: שורות שמופרדות בירידות שורה, ושדות שמופרדים בפסיקים. זה הרעיון. בפועל נתקלים במחרוזות במירכאות שמכילות פסיקים, בשדות עם ירידות שורה בתוכם, במוסכמות אזוריות שונות (פסיק מול נקודה-פסיק), בקבצים שנשמרו מ-Excel וכוללים BOM. יש מספיק מקרי קצה כדי שכתיבת line.split(",") משלכם תהיה כמעט תמיד טעות.
המודול המובנה csv של Python מטפל בכל זה. לקבצים קטנים ובינוניים כמעט לא תצטרכו שום דבר אחר.
קריאת CSV עם csv.reader
csv.reader מחזיר כל שורה כרשימה של מחרוזות:
import csv
with open("people.csv", newline="") as f:
reader = csv.reader(f)
for row in reader:
print(row)
כמה פרטים לא מובנים מאליהם:
- העבירו תמיד
newline=""ל-open. מודול ה-csv מטפל בסימני סוף שורה בעצמו; בלי זה תקבלו שורות ריקות מיותרות ב-Windows. - כל ערך הוא מחרוזת.
"42"נשאר מחרוזת עד שקוראים עליו ל-int(...). ל-CSV אין טיפוסים. - שורת הכותרות היא עוד שורה רגילה. אם בקובץ יש כותרות, דלגו על השורה הראשונה ידנית או עברו ל-
DictReader.
דילוג על שורת הכותרות
import csv
with open("people.csv", newline="") as f:
reader = csv.reader(f)
headers = next(reader) # שולף את השורה הראשונה
for row in reader:
print(row)
next(reader) מקדם את האיטרטור בצעד אחד ומחזיר את השורה הזו.
קריאה כ-dicts עם DictReader
csv.DictReader מתייחס לשורה הראשונה ככותרות ומחזיר כל שורה שאחריה כ-dict:
import csv
with open("people.csv", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
print(row["name"], row["email"])
כמעט תמיד זה מה שאתם רוצים. שמות העמודות מתעדים את עצמם, ושינוי סדר העמודות בקובץ המקור לא שובר את הקוד.
אם בקובץ אין כותרות, העבירו אותן במפורש עם fieldnames=["name", "email", ...].
כתיבת CSV עם csv.writer
csv.writer הופך שורות (רשימות) לשורות CSV:
import csv
rows = [
["name", "age", "city"],
["Rosa", 30, "Lisbon"],
["Ada", 36, "London"],
]
with open("out.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerows(rows)
writerow(row) כותב שורה אחת; writerows(rows) כותב אוסף שלם בבת אחת. שניהם מוסיפים מירכאות לשדות אוטומטית כשהם מכילים פסיקים, מירכאות או ירידות שורה, כך שלא צריך לחשוב על זה.
כתיבת dicts עם DictWriter
כשהנתונים כבר בצורת dict, DictWriter חוסך את שלב ההמרה לרשימה:
import csv
people = [
{"name": "Rosa", "age": 30, "city": "Lisbon"},
{"name": "Ada", "age": 36, "city": "London"},
]
with open("out.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "age", "city"])
writer.writeheader()
writer.writerows(people)
הארגומנט fieldnames קובע גם את הכותרת וגם את סדר העמודות. מפתחות ב-dicts שלא מופיעים ב-fieldnames נזרקים בשקט (או שאפשר לגרום לשגיאה עם extrasaction="raise").
מפרידים שונים ומירכאות
לא כל "CSV" משתמש בפסיקים. באזורים באירופה נפוץ ;, קבצים מופרדי טאבים משתמשים ב-\t, ומערכות מסוימות משתמשות ב-|. העבירו ארגומנט delimiter=:
import csv
with open("data.tsv", newline="") as f:
reader = csv.reader(f, delimiter="\t")
for row in reader:
print(row)
לקבצים עם כללי מירכאות חריגים, csv.register_dialect(...) מאפשר להגדיר פעם אחת ולהשתמש שוב. לרוב הקבצים, ברירות המחדל יחד עם delimiter= מספיקות.
קידוד
קובצי CSV הם טקסט, ולכן יש להם קידוד. UTF-8 היא ברירת המחדל המודרנית; קבצים שמקורם ב-Excel ב-Windows משתמשים לפעמים ב-cp1252 או כוללים UTF-8 BOM. היו מפורשים:
with open("data.csv", newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
...
אם מופיעה UnicodeDecodeError, הקובץ לא בקידוד שניחשתם. החשודים הרגילים שכדאי לנסות: utf-8-sig (מטפל ב-BOM של Excel), cp1252 או latin-1.
הפיכת שורות CSV לטיפוסים שימושיים
מאחר שכל ערך מגיע כמחרוזת, הפענוח באחריותכם:
(StringIO מאפשר להריץ את הדוגמה בלי קובץ אמיתי; בקוד אמיתי הייתם כותבים open(path).)
לקובצי CSV עם טיפוסים מסובכים (תאריכים, מספרים שיכולים להיות ריקים, true/false בעשרה איותים שונים), שקלו את pandas: יש בו מוסכמות מובנות לרובם.
מתי לעבור ל-pandas
pandas.read_csv(path) מחזירה DataFrame, שהוא המבנה הנכון ברגע שרוצים:
- לסנן שורות:
df[df["active"] == True] - לבצע אגרגציה:
df.groupby("city")["age"].mean() - לחבר עם טבלה אחרת
- לכתוב חזרה לקובץ עם עיצוב פשוט
import pandas as pd
df = pd.read_csv("people.csv")
adults = df[df["age"] >= 18]
adults.to_csv("adults.csv", index=False)
pandas מוגזמת לקריאות קטנות וליניאריות, והיא תלות כבדה (התקינו אותה עם pip בסביבה וירטואלית). אבל לכל מה שנראה כמו נתונים, זה הכלי שרוב אנליסטי ה-Python פונים אליו.
עיבוד בהזרמה של קבצים גדולים מאוד
csv.reader כבר עצלן: הוא קורא שורה אחת בכל פעם. שמרו על זה בכך שתעברו עליו בלולאה (ולא תקראו ל-list(reader) מראש), והזיכרון יישאר יציב בלי קשר לגודל הקובץ:
import csv
with open("huge.csv", newline="") as f:
reader = csv.DictReader(f)
error_count = 0
for row in reader:
if row["status"] == "error":
error_count += 1
print(f"Found {error_count} errors.")
זה מטפל בקובץ של 10 GB באותה קלות כמו בקובץ של 10 KB, כל עוד לא צוברים את השורות לתוך רשימה.
כמה הרגלים
- העבירו תמיד
newline=""ל-openכשקוראים או כותבים קובצי CSV. - השתמשו ב-
DictReader/DictWriterבכל פעם שיש בקובץ כותרות: זה קריא יותר מאינדקסים מספריים. - היו מפורשים לגבי הקידוד, במיוחד בקבצים מ-Excel או ממקורות שאינם באנגלית.
- המירו טיפוסים כבר בשלב הקריאה, כדי שהקוד שבהמשך לא יצטרך.
- עברו ל-pandas ברגע שרוצים לנתח את הנתונים, ולא רק להעביר אותם.
בהמשך
עכשיו אתם יודעים לקרוא JSON ו-CSV. המיומנות המעשית האחרונה שנכסה היא הבאת נתונים דרך הרשת: זה המסמך הבא, על בקשות HTTP עם הספרייה requests.
שאלות נפוצות
איך קוראים קובץ CSV ב-Python?
השתמשו במודול המובנה csv. csv.reader מחזיר כל שורה כרשימה של מחרוזות; csv.DictReader משתמש בשורה הראשונה ככותרות ומחזיר כל שורה כ-dict. פתחו את הקובץ עם newline='' כדי ש-Python לא תשבש את סימני סוף השורה: with open('data.csv', newline='') as f:.
איך כותבים קובץ CSV ב-Python?
חברו את csv.writer לקובץ שנפתח במצב כתיבה עם newline=''. קראו ל-writer.writerow([...]) עבור כל שורה, או ל-writer.writerows([[...], [...]]) עבור קבוצת שורות. לנתונים בצורת dict, השתמשו ב-csv.DictWriter: הוא מטפל בכותרות אוטומטית.
להשתמש במודול csv או ב-pandas?
השתמשו ב-csv לקריאות וכתיבות מהירות, לקבצים שמעבדים שורה אחרי שורה, וכשלא רוצים עוד תלות. השתמשו ב-pandas כשצריך לסנן, לקבץ או לחבר טבלאות, או כשהקובץ גדול מספיק כדי שפעולות וקטוריות ישנו. שניהם מטפלים באותם קבצים; הבחירה תלויה במה שעושים עם הנתונים אחרי הטעינה.
למה ב-Windows יש לי שורות ריקות בין השורות בקובץ ה-CSV?
פתחתם את הקובץ בלי newline=''. המודול csv כותב סימני סוף שורה משלו; בלי הארגומנט הזה, Python מוסיפה עוד סימנים ב-Windows. פתחו תמיד קובצי CSV עם open(path, newline=''), גם לקריאה וגם לכתיבה.