Menu

בקשות HTTP ב-Python: שימוש בספריית requests (GET, POST, JSON)

איך שולחים בקשות HTTP ב-Python עם ספריית requests: GET, POST, פרמטרי שאילתה, כותרות, גוף JSON וטיפול בשגיאות.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

להביא נתונים מהאינטרנט בכמה שורות

רוב תוכניות ה-Python האמיתיות צריכות בשלב כלשהו לדבר עם משהו דרך הרשת: REST API, שירות מזג אוויר, endpoint של GitHub, הורדה. הספרייה הסטנדרטית יודעת לעשות את זה (דרך urllib), אבל הכלי המקובל בעולם ה-Python הוא ספרייה חיצונית בשם requests. ה-API שלה ידידותי כל כך שהיא שווה את ה-pip install היחיד.

pip install requests

אם אתם עוד לא עובדים בתוך סביבה וירטואלית, הקימו אחת קודם: היא שומרת את ההתקנה הזו בגבולות הפרויקט שלכם.

בקשת GET ראשונה

שלוש שורות: קוראים ל-get, בודקים את קוד הסטטוס, מסתכלים על הגוף. response.text הוא גוף התשובה כמחרוזת. כאן הוא מקוצר, כי ה-JSON המלא ארוך.

קודי סטטוס שכדאי לזהות ברמת דף עזר:

  • 200: OK, הכל עבד.
  • 201: Created, נוצר (תשובה נפוצה ל-POST).
  • 301 / 302: הפניות; requests עוקבת אחריהן אוטומטית כברירת מחדל.
  • 400: בקשה שגויה; משהו במה ששלחתם היה לא תקין.
  • 401 / 403: לא מזוהה / אין הרשאה.
  • 404: המשאב לא קיים.
  • 429: הגבלת קצב; האטו.
  • 500: שגיאת שרת.

פענוח תשובת JSON

כשה-endpoint מחזיר JSON, קראו ל-.json() על התשובה: היא מפענחת את הגוף ומגישה לכם dict (או רשימה):

מאחורי הקלעים, .json() זהה ל-json.loads(response.text): פשוט קיצור דרך למקרה הנפוץ.

שליחת פרמטרי שאילתה

אל תדביקו ידנית ?key=value&... לכתובת. העבירו dict ל-params=:

requests מטפלת בשבילכם בקידוד ה-URL: רווחים, תווים מיוחדים ו-Unicode עובדים בבטחה.

הכתובת שנשלחה בפועל זמינה ב-response.url, שימושי לדיבוג.

בקשות POST עם גוף JSON

לשליחת נתונים (יצירת משאבים, שליחת טפסים, קריאה ל-endpoints שמשנים משהו) השתמשו ב-requests.post:

import requests

payload = {
    "title": "Docs update",
    "body": "Added HTTP requests page.",
    "labels": ["docs"],
}

response = requests.post(
    "https://api.example.com/issues",
    json=payload,
    headers={"Authorization": "Bearer YOUR_TOKEN"},
)

print(response.status_code)
print(response.json())

הארגומנט json= עושה שני דברים: הוא ממיר את payload ל-JSON ומגדיר Content-Type: application/json. אפשר לעשות את שניהם ידנית עם data=json.dumps(payload) וכותרת מפורשת, אבל json= הוא הקיצור המקובל.

לנתונים בקידוד טופס (מה שטופס HTML קלאסי שולח), השתמשו במקום זאת ב-data=:

requests.post("https://example.com/login", data={"user": "rosa", "password": "..."})

כותרות

העבירו כל כותרת מותאמת אישית בתור dict:

import requests

response = requests.get(
    "https://api.example.com/profile",
    headers={
        "Authorization": "Bearer abc123",
        "User-Agent": "my-tool/1.0",
    },
)

רוב ה-APIs רוצים כותרת Authorization לאימות. השיטה המדויקת (Bearer, Basic, Token) מופיעה בתיעוד שלהם.

Timeouts הם לא אופציונליים

כברירת מחדל, requests תחכה לתשובה לנצח. בתוכנית אמיתית, זה הופך שרת לא יציב לסקריפט תקוע. העבירו תמיד timeout:

import requests

try:
    response = requests.get("https://api.example.com/slow", timeout=5)
except requests.Timeout:
    print("Server took too long.")

המספר הוא בשניות. timeout=5 פירושו "לוותר אם אין תשובה תוך 5 שניות". אפשר להעביר tuple של (connect_timeout, read_timeout) לשליטה מדויקת יותר.

טיפול בשגיאות

יכולים לקרות שני סוגים של בעיות:

  1. שגיאות ברמת HTTP (4xx, 5xx): השרת ענה, אבל התשובה היא שגיאה. response.status_code אומר לכם.
  2. בעיות ברמת הרשת: timeouts, כשלי DNS, שרתים שלא ניתן להגיע אליהם. אלה זורקות חריגות.

הדפוס המקובל משלב את שניהם:

raise_for_status() לא עושה כלום בתשובות 2xx, וזורקת חריגה בכל מקרה אחר. RequestException היא מחלקת הבסיס לכל שגיאה ש-requests זורקת: תפיסה אחת ל"כל דבר שהשתבש בתקשורת עם ה-endpoint הזה".

הורדת קובץ

בהורדות בינאריות גדולות, הזרימו את התשובה כדי שהיא לא תשב בזיכרון:

import requests

url = "https://example.com/large.zip"

with requests.get(url, stream=True, timeout=30) as r:
    r.raise_for_status()
    with open("large.zip", "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)

stream=True אומר ל-requests לא לטעון את הגוף מראש. iter_content(chunk_size=...) מניבה את הגוף חתיכה אחרי חתיכה, ואתם כותבים אותה ישר לדיסק.

Sessions: שימוש חוזר בחיבורים ובברירות מחדל

אם אתם עומדים לשלוח כמה בקשות לאותו שירות, השתמשו ב-Session. היא משתמשת שוב באותו חיבור TCP (מהיר יותר) ומאפשרת להגדיר ברירות מחדל פעם אחת:

import requests

session = requests.Session()
session.headers.update({"Authorization": "Bearer abc123"})

# כל בקשה דרך ה-session הזה נושאת את הכותרת.
a = session.get("https://api.example.com/users/1")
b = session.get("https://api.example.com/users/2")
c = session.post("https://api.example.com/users", json={"name": "Rosa"})

בסקריפטים שפונים לאותו API עשרות פעמים, session היא האצה משמעותית.

דוגמה מציאותית: לקוח GitHub קטן

הבאת הגרסה האחרונה (release) של מאגר:

import requests

def latest_release(owner, repo):
    url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    data = response.json()
    return {
        "tag": data["tag_name"],
        "name": data["name"],
        "published": data["published_at"],
        "url": data["html_url"],
    }

release = latest_release("python", "cpython")
print(release)

פחות מחמש עשרה שורות: בונים את הכתובת, שולחים בקשה, בודקים שגיאות ושולפים את השדות שמעניינים אתכם. זו הצורה של רוב לקוחות ה-API שתכתבו.

ומה עם urllib?

urllib.request של הספרייה הסטנדרטית יכולה לעשות כל מה ש-requests עושה, ביותר שורות ובנוחות פחותה. אם אתם ממש לא יכולים להוסיף תלות, היא שם:

import json
import urllib.request

with urllib.request.urlopen("https://api.github.com/repos/python/cpython") as r:
    data = json.loads(r.read().decode("utf-8"))

print(data["name"])

לכל דבר מעבר לסקריפט מהיר, requests (או httpx אם צריך async) שווה את ההתקנה.

כמה הרגלים

  • הגדירו תמיד timeout. בלי יוצאים מן הכלל.
  • השתמשו ב-raise_for_status() בסקריפטים שמצפים להצלחה: היא הופכת תשובה גרועה לחריגה רועשת.
  • העבירו dicts ל-params= ול-json=, לא מחרוזות שבניתם ביד.
  • עטפו קריאות קשורות ב-Session כשאתם פונים לאותו API פעמים רבות.
  • רשמו ללוג את response.status_code ואת response.text בזמן דיבוג: הגוף בדרך כלל אומר לכם בדיוק מה לא מצא חן בעיני השרת.

הבא בתור: תאריכים ושעות

עם requests בארגז הכלים, אתם יכולים לדבר עם כל web API מודרני, להוריד קבצים ולבנות אינטגרציות קטנות בין שירותים. יחד עם העמודים על JSON ו-CSV שלפני העמוד הזה, יש לכם עכשיו את כל הלולאה של "להביא נתונים, לקרוא אותם, לעשות איתם משהו ולכתוב אותם בחזרה": הצורה של מספר עצום של סקריפטים אמיתיים ב-Python. לרוב הנתונים האלה יש חותמת זמן, והעמוד הבא מסביר איך Python מייצגת תאריכים ושעות, ואילו מלכודות של אזורי זמן כדאי לעקוף.

שאלות נפוצות

איך שולחים בקשת HTTP ב-Python?

התקינו את ספריית requests עם pip install requests, ואז קראו ל-requests.get(url) לבקשת GET או ל-requests.post(url, json=...) לבקשת POST. לאובייקט התשובה יש .status_code, .text, .json() ו-.headers. לדוגמה: r = requests.get('https://api.example.com/users/1').

להשתמש ב-requests או ב-urllib?

ב-requests לכל מה שכותבים ביד: ה-API שלה ידידותי בהרבה. urllib מובנית ומתאימה כשאי אפשר להוסיף תלות, אבל היא דורשת יותר קוד לדברים כמו גוף JSON ו-sessions. בפרודקשן, צוותים רבים משתמשים גם ב-httpx (ספרייה שתואמת ל-requests ותומכת ב-async).

איך שולחים JSON בבקשת POST ב-Python?

העבירו json={'key': 'value'} ל-requests.post(...). requests ממירה את ה-dict ל-JSON ומגדירה בשבילכם Content-Type: application/json. אל תעבירו גם data= וגם json=: בחרו אחד.

איך מטפלים בשגיאות עם ספריית requests?

בדקו את response.status_code (200 פירושו הצלחה), או קראו ל-response.raise_for_status() כדי לזרוק חריגה על 4xx/5xx. בעיות ברמת הרשת (timeouts, כשלי DNS) זורקות תת-מחלקות של requests.RequestException: תפסו אותה כדי לכסות את שני המקרים.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל