מה זה?

עד עכשיו טענו נתונים מקבצים סטטיים (read_csv). אבל ETL אמיתי (Extract-Transform-Load) שולף נתונים ממקורות חיים — API חיצוני, מסד נתונים — מעבד אותם (כל מה שלמדנו עד כה), וכותב את התוצאה בחזרה למקום קבוע. requests הוא ה-Extract מ-API; to_sql הוא ה-Load למסד נתונים — שני הקצוות של כל צנרת ETL.

מילות מפתח שחשוב לזכור

• Extract — שליפת נתונים גולמיים ממקור חיצוני (API, קובץ, DB)

• Transform — עיבוד וניקוי הנתונים (כל מה שנלמד בשיעורים הקודמים ביחידה)

• Load — כתיבת התוצאה הסופית ליעד קבוע (DB, קובץ)

• requests — ספריית Python לביצוע קריאות HTTP (מקביל ל-HttpClient ב-C#)

• to_sql() — כותב DataFrame ישירות לטבלת SQL, בלי לבנות שאילתת INSERT ידנית

import requests
import pandas as pd

# Extract — fetching from an external API, just like HTTP GET from the ASP.NET Core unit
response = requests.get("https://api.example.com/orders")
data = response.json()              # decode JSON into a list of dicts
df = pd.DataFrame(data)             # direct conversion to a DataFrame

# Extract from an existing database
import sqlalchemy
engine = sqlalchemy.create_engine("postgresql://user:pass@localhost/mydb")
df_from_db = pd.read_sql("SELECT * FROM orders WHERE status = 'completed'", engine)
# Transform — everything already learned in this unit
df["OrderDate"] = pd.to_datetime(df["OrderDate"])
df_clean = df.dropna(subset=["CustomerId"])
summary = df_clean.groupby("CustomerId").agg(Total=("Amount", "sum")).reset_index()
# Load — writing back to the database
summary.to_sql("customer_summary", engine, if_exists="replace", index=False)
# creates/replaces the customer_summary table with the processed result — no manual SQL INSERT

הסבר עיקרי

requests.get מקביל ל-HttpClient ב-C#, אבל בלי async מפורש — קריאת HTTP בפייתון עם requests דומה מאוד לרעיון שכבר מוכר מיחידת ASP.NET Core (בקשה, תשובה, .json() לפענוח) — רק שהתחביר הבסיסי (לא-async כברירת מחדל) פשוט יותר, מתאים לסקריפטים חד-פעמיים/מתוזמנים בניגוד לשרת שמשרת הרבה בקשות במקביל.

to_sql הוא Load אמיתי, לא רק “שמירת קובץ” — בניגוד ל-to_csv (כתיבה לקובץ, פשוטה יותר), to_sql כותב ישירות לטבלת DB אמיתית — שימושי כשתוצאת העיבוד צריכה “לחיות” במסד נתונים production, נגישה לשירותים אחרים (כמו ה-API ב-C# מיחידות קודמות) לא רק כקובץ מקומי.

if_exists="replace" הוא החלטה חשובה — כשה-Load רץ שוב (למשל בהרצה מתוזמנת יומית, נושא השיעור הבא), האם התוצאה החדשה מחליפה את הישנה ("replace"), מתווספת אליה ("append"), או שהריצה נכשלת אם הטבלה כבר קיימת ("fail", ברירת המחדל)? הבחירה תלויה בטבע הנתונים — דוח יומי-מצטבר לרוב “replace” (הדוח החדש מייצג את כל התמונה), לוג אירועים לרוב “append” (מוסיפים לרשומה קיימת).

יתרונות

requests נותן גישה פשוטה למקורות נתונים חיים (APIs), לא רק קבצים סטטיים; to_sql כותב תוצאות ישירות ל-DB בלי SQL ידני; שילוב Extract→Transform→Load בסקריפט אחד ממחיש את כל מחזור החיים של הנתונים.

חסרונות

requests.get בלי טיפול שגיאות (timeout, סטטוס לא-2xx) עלול לקרוס בשקט על בעיות רשת; to_sql על DataFrame ענק עלול להיות איטי בהשוואה לכלי Bulk Insert ייעודיים.

נקודות חשובות

• Extract-Transform-Load: שליפה ממקור, עיבוד, כתיבה ליעד — סדר תמידי

• requests.get(url).json() שולף ומפענח מ-API; pd.read_sql שולף מ-DB קיים

• to_sql כותב DataFrame ישירות לטבלה, עם if_exists (replace/append/fail) קובע התנהגות בהרצה חוזרת

• Extract/Load הם שני הקצוות; Transform הוא כל מה שכבר נלמד קודם ביחידה

טעויות נפוצות

• לא לבדוק response.status_code לפני .json() — קריסה לא-ברורה אם ה-API החזיר שגיאה

• לבחור if_exists="fail" (ברירת מחדל) בטעות בהרצה חוזרת ולקבל שגיאה במקום עדכון מצופה

• לשכוח שה-Transform (ניקוי, סינון) צריך לקרות לפני ה-Load, לא אחריו

סיכום

ETL הוא Extract (שליפה ממקור — API עם requests, או DB עם read_sql), Transform (עיבוד — כל מה שנלמד קודם ביחידה: merge/groupby/ניקוי), ו-Load (כתיבה ליעד — to_sql ל-DB). if_exists קובע איך הLoad מתנהג בהרצות חוזרות — replace/append/fail, החלטה שתלויה בטבע הדוח. זהו המסגרת המלאה שהפרויקט המסכם של היחידה יבנה עליה.

דוקומנטציה רשמית

Requests — Documentation

Pandas — to_sql


תרגילים

תרגיל 1 — Extract מ-API

המשימה: בחרו API ציבורי חינמי (למשל JSONPlaceholder), שלפו נתונים עם requests.get, והמירו ל-DataFrame.

בדיקה: ה-DataFrame מכיל את הנתונים מה-API, עם עמודות תואמות למבנה ה-JSON שהתקבל.

תרגיל 2 — Load ל-SQL

המשימה: קחו DataFrame מעובד (מתרגיל קודם ביחידה) וכתבו אותו לטבלת SQLite/Postgres מקומית עם to_sql.

בדיקה: שאילתה ישירה על הטבלה (SELECT * FROM ...) מציגה את הנתונים שנכתבו.


פרויקט מסכם

המשימה: בנו סקריפט ETL מלא: Extract מ-API ציבורי → Transform (ניקוי + groupby) → Load ל-DB מקומי.

דרישות:

  1. Extract עם requests, כולל בדיקת status_code לפני עיבוד
  2. Transform: ניקוי (fillna/dropna לפי הצורך) וקיבוץ (groupby+agg) לסיכום משמעותי
  3. Load עם to_sql, ובחירה מנומקת של if_exists (replace או append) לפי טבע הנתונים

בדיקה: הרצת הסקריפט מקצה לקצה מייצרת טבלה ב-DB עם נתונים מעובדים ונכונים; הרצה חוזרת מתנהגת כצפוי לפי if_exists שנבחר.

מה בפרק הבא

בפרק הבא — Scheduling ולוגים: איך מריצים את סקריפט ה-ETL אוטומטית מדי יום (cron/APScheduler), עם לוגים מובנים שמתעדים מה קרה בכל הרצה.