Last modified: June 06, 2026
This article is written in: 🇵🇱
HTTP (Hypertext Transfer Protocol) to protokół warstwy aplikacji w modelu OSI, używany głównie do przesyłania danych między klientem (zwykle przeglądarką internetową) a serwerem. Protokół ten opiera się na modelu żądanie-odpowiedź, gdzie klient wysyła zapytanie HTTP, a serwer zwraca odpowiedź. HTTP jest protokółem bezstanowym, co oznacza, że każde żądanie jest niezależne i serwer nie utrzymuje informacji o poprzednich żądaniach.
HTTP definiuje kilka metod, które określają, jak klient komunikuje się z serwerem:
| Metoda | Opis |
| GET | Służy do pobierania danych. Powinno być idempotentne, co oznacza, że nie zmienia stanu. |
| POST | Używane do wysyłania danych na serwer, np. tworzenia nowych zasobów. Nie jest idempotentne. |
| PUT | Służy do aktualizacji istniejących zasobów lub ich tworzenia, jeśli nie istnieją. |
| DELETE | Usuwa wskazany zasób z serwera. |
| HEAD | Podobne do GET, ale zwraca tylko nagłówki odpowiedzi bez treści. |
| OPTIONS | Pyta serwer o dostępne metody dla danego zasobu. |
Każda odpowiedź HTTP zawiera kod statusu, który wskazuje na wynik żądania:
| Kod | Opis |
| 200 OK | Żądanie zakończyło się sukcesem. |
| 201 Created | Zasób został pomyślnie stworzony (np. przez POST). |
| 204 No Content | Żądanie zakończyło się sukcesem, ale odpowiedź nie zawiera treści. |
| 400 Bad Request | Żądanie było niepoprawne lub niemożliwe do zrealizowania. |
| 401 Unauthorized | Klient musi się uwierzytelnić. |
| 403 Forbidden | Klient nie ma uprawnień do dostępu do zasobu. |
| 404 Not Found | Zasób nie został znaleziony. |
| 500 Internal Server Error | Wewnętrzny błąd serwera. |
W Pythonie jednym z najczęściej używanych modułów do obsługi żądań HTTP jest requests. Jest on prosty w użyciu, a jednocześnie potężny, umożliwiając wykonywanie skomplikowanych operacji HTTP z łatwością.
Poniżej rozszerzony przykład, który pokazuje, jak wykonać żądanie GET oraz jak zarządzać nagłówkami i parametrami:
import requests
url = "https://jsonplaceholder.typicode.com/posts"
headers = {
'User-Agent': 'my-app/0.0.1',
'Accept': 'application/json'
}
params = {
'userId': 1
}
response = requests.get(url, headers=headers, params=params)
print(f'Status code: {response.status_code}') # Sprawdzenie statusu odpowiedzi
print(f'Response headers: {response.headers}') # Nagłówki odpowiedzi
print(f'Response JSON: {response.json()}') # Zawartość odpowiedzi w formacie JSON
W powyższym przykładzie dodano nagłówki oraz parametry zapytania, co pokazuje elastyczność modułu requests. Dzięki temu można modyfikować żądania w zależności od potrzeb aplikacji.
Python dostarcza moduł http.server, który umożliwia uruchomienie prostego serwera HTTP. Jest to szczególnie przydatne w fazie rozwoju, kiedy chcemy przetestować naszą stronę lub aplikację lokalnie. Serwer ten jest jednak prosty i nie powinien być używany w środowisku produkcyjnym, ponieważ nie zapewnia zabezpieczeń ani wydajności odpowiedniej dla rzeczywistych aplikacji.
Poniższy przykład pokazuje, jak uruchomić prosty serwer HTTP, który serwuje pliki z bieżącego katalogu:
import http.server
import socketserver
PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print(f"Serving files from the current directory at port {PORT}")
httpd.serve_forever()
http.server.SimpleHTTPRequestHandler: Domyślna klasa obsługująca żądania GET i HEAD, serwująca pliki z katalogu, w którym uruchomiono serwer.socketserver.TCPServer: Klasa obsługująca nasłuchiwanie na porcie TCP. W powyższym przykładzie serwer nasłuchuje na porcie 8000.Aby dostosować serwer do specyficznych potrzeb, można rozszerzyć klasę SimpleHTTPRequestHandler, np. aby obsługiwać dodatkowe metody HTTP, takie jak POST. Poniżej przedstawiam przykład niestandardowej obsługi żądań:
class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"Custom GET response")
def do_POST(self):
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
print(f"Received POST data: {post_data.decode('utf-8')}")
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"POST received")
PORT = 8000
with socketserver.TCPServer(("", PORT), CustomHTTPRequestHandler) as httpd:
print(f"Serving custom responses at port {PORT}")
httpd.serve_forever()
do_GET: Przykład niestandardowej odpowiedzi na żądanie GET, w której ręcznie wysyłamy nagłówki oraz zawartość odpowiedzi.do_POST: Odbiera dane z żądania POST, przetwarza je, a następnie zwraca odpowiedź. Takie podejście daje pełną kontrolę nad tym, jak serwer ma odpowiadać na różne typy żądań, co może być szczególnie przydatne podczas tworzenia API lub prostych aplikacji webowych bez konieczności korzystania z zewnętrznych frameworków.
Mimo że serwer dostarczany przez moduł http.server jest wygodny, jego ograniczenia sprawiają, że nie nadaje się do użytku produkcyjnego:
requestsimport requests
base_url = "https://jsonplaceholder.typicode.com"
# POST — tworzenie nowego zasobu
nowy_post = {
"title": "Nowy post",
"body": "Treść posta",
"userId": 1
}
odpowiedz = requests.post(f"{base_url}/posts", json=nowy_post)
print(odpowiedz.status_code) # 201 Created
print(odpowiedz.json())
# PUT — aktualizacja zasobu
aktualizacja = {"title": "Zmieniony tytuł", "body": "Nowa treść", "userId": 1}
odpowiedz = requests.put(f"{base_url}/posts/1", json=aktualizacja)
print(odpowiedz.status_code) # 200 OK
# DELETE — usunięcie zasobu
odpowiedz = requests.delete(f"{base_url}/posts/1")
print(odpowiedz.status_code) # 200 OK
import requests
from requests.exceptions import Timeout, ConnectionError, HTTPError
try:
odpowiedz = requests.get("https://example.com/api/data", timeout=5)
odpowiedz.raise_for_status() # Rzuca HTTPError dla 4xx i 5xx
print(odpowiedz.json())
except Timeout:
print("Żądanie przekroczyło limit czasu")
except ConnectionError:
print("Błąd połączenia — brak internetu lub błędny host")
except HTTPError as e:
print(f"Błąd HTTP: {e.response.status_code}")
Sesja (Session) zachowuje cookies i nagłówki między żądaniami:
import requests
with requests.Session() as sesja:
# Ustawienie wspólnych nagłówków dla wszystkich żądań w sesji
sesja.headers.update({
"Authorization": "Bearer moj-token",
"Accept": "application/json"
})
# Pierwsze żądanie — logowanie
odpowiedz = sesja.post("https://api.example.com/login",
json={"user": "jan", "password": "tajne"})
# Kolejne żądania używają tych samych cookies i nagłówków
profil = sesja.get("https://api.example.com/profile")
print(profil.json())
urllib — standardowa bibliotekaDla prostych przypadków nie potrzebujemy requests — wystarczy wbudowany urllib.request:
import urllib.request
import urllib.parse
import json
# GET
with urllib.request.urlopen("https://jsonplaceholder.typicode.com/posts/1") as resp:
dane = json.loads(resp.read().decode())
print(dane["title"])
# POST
dane_post = json.dumps({"title": "test", "body": "treść", "userId": 1}).encode()
req = urllib.request.Request(
"https://jsonplaceholder.typicode.com/posts",
data=dane_post,
headers={"Content-Type": "application/json"},
method="POST"
)
with urllib.request.urlopen(req) as resp:
print(resp.status) # 201
import requests
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
# Basic Auth
odpowiedz = requests.get("https://httpbin.org/basic-auth/user/pass",
auth=HTTPBasicAuth("user", "pass"))
print(odpowiedz.status_code) # 200
# Token Bearer (JWT)
headers = {"Authorization": "Bearer eyJhbGci..."}
odpowiedz = requests.get("https://api.example.com/secure", headers=headers)
# API Key w parametrach zapytania
odpowiedz = requests.get("https://api.example.com/data",
params={"api_key": "moj-klucz-api"})
| Zakres | Kategoria | Najczęstsze kody |
| 1xx | Informacyjne | 100 Continue, 101 Switching Protocols |
| 2xx | Sukces | 200 OK, 201 Created, 204 No Content |
| 3xx | Przekierowanie | 301 Moved Permanently, 302 Found, 304 Not Modified |
| 4xx | Błąd klienta | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found |
| 5xx | Błąd serwera | 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable |
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# Konfiguracja automatycznego ponowienia prób
session = requests.Session()
retry = Retry(
total=3, # maksymalnie 3 próby
backoff_factor=0.5, # opóźnienie: 0.5s, 1s, 2s
status_forcelist=[500, 502, 503, 504] # ponawiaj dla tych kodów
)
adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)
# Użycie sesji z retry
try:
odpowiedz = session.get("https://api.example.com/data", timeout=10)
odpowiedz.raise_for_status() # Rzuci wyjątek dla kodów 4xx/5xx
dane = odpowiedz.json()
except requests.exceptions.Timeout:
print("Przekroczono limit czasu!")
except requests.exceptions.HTTPError as e:
print(f"Błąd HTTP: {e.response.status_code}")
except requests.exceptions.ConnectionError:
print("Brak połączenia z serwerem!")
| Biblioteka | Synchroniczna | Asynchroniczna | HTTP/2 | Wbudowana? | Kiedy używać |
urllib |
Tak | Nie | Nie | Tak | Proste skrypty bez zależności |
requests |
Tak | Nie | Nie | Nie | Większość aplikacji (standard) |
httpx |
Tak | Tak | Tak | Nie | Nowoczesne API, async+sync |
aiohttp |
Nie | Tak | Nie | Nie | Wysokowydajne async aplikacje |