Pobieranie danych z internetu w kilku liniach
Większość prawdziwych programów w Pythonie prędzej czy później musi porozmawiać z czymś przez sieć: z REST API, serwisem pogodowym, endpointem GitHuba, serwerem z plikiem do pobrania. Biblioteka standardowa to potrafi (przez urllib), ale w świecie Pythona standardem de facto jest zewnętrzna biblioteka requests. Jej API jest na tyle przyjaźniejsze, że warto wykonać to jedno pip install.
pip install requests
Jeśli nie pracujesz jeszcze w środowisku wirtualnym, najpierw je utwórz: dzięki temu instalacja dotyczy tylko twojego projektu.
Pierwsze żądanie GET
Trzy linie: wywołaj get, sprawdź kod statusu, obejrzyj treść. response.text to treść odpowiedzi jako napis. Tutaj jest przycięta, bo pełny JSON jest długi.
Kody statusu, które warto rozpoznawać na poziomie ściągi:
- 200: OK, wszystko zadziałało.
- 201: Created, utworzono zasób (częsta odpowiedź na POST).
- 301 / 302: przekierowania,
requestsdomyślnie automatycznie za nimi podąża. - 400: błędne żądanie, coś w wysłanych danych było nie tak.
- 401 / 403: brak uwierzytelnienia / brak uprawnień.
- 404: zasób nie istnieje.
- 429: przekroczony limit żądań, zwolnij.
- 500: błąd serwera.
Parsowanie odpowiedzi JSON
Gdy endpoint zwraca JSON, wywołaj na odpowiedzi .json(): parsuje treść i daje ci słownik (albo listę):
Pod spodem .json() to to samo co json.loads(response.text), tylko skrót dla typowego przypadku.
Wysyłanie parametrów zapytania
Nie doklejaj ręcznie ?key=value&... do adresu URL. Przekaż słownik do params=:
requests samo zajmuje się kodowaniem URL: spacje, znaki specjalne i Unicode działają bezpiecznie.
Faktyczny adres, który został wysłany, jest dostępny w response.url, co przydaje się przy debugowaniu.
Żądania POST z danymi JSON
Do wysyłania danych (tworzenia zasobów, wysyłania formularzy, wywoływania endpointów, które coś zmieniają) użyj 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())
Argument json= robi dwie rzeczy: serializuje payload do JSON i ustawia Content-Type: application/json. Oba kroki dałoby się zrobić ręcznie przez data=json.dumps(payload) i jawny nagłówek, ale json= to idiomatyczny skrót.
Dla danych kodowanych jak formularz (takich, jakie wysyła klasyczny formularz HTML) użyj zamiast tego data=:
requests.post("https://example.com/login", data={"user": "rosa", "password": "..."})
Nagłówki
Własne nagłówki przekazuj jako słownik:
import requests
response = requests.get(
"https://api.example.com/profile",
headers={
"Authorization": "Bearer abc123",
"User-Agent": "my-tool/1.0",
},
)
Większość API wymaga do uwierzytelnienia nagłówka Authorization. Dokładny schemat (Bearer, Basic, Token) znajdziesz w ich dokumentacji.
Limity czasu nie są opcjonalne
Domyślnie requests będzie czekać na odpowiedź w nieskończoność. W prawdziwym programie niestabilny serwer zamienia się wtedy w zawieszony skrypt. Zawsze przekazuj timeout:
import requests
try:
response = requests.get("https://api.example.com/slow", timeout=5)
except requests.Timeout:
print("Server took too long.")
Liczba oznacza sekundy. timeout=5 znaczy "poddaj się, jeśli w ciągu 5 sekund nie będzie odpowiedzi". Dla większej kontroli możesz przekazać krotkę (connect_timeout, read_timeout).
Obsługa błędów
Mogą wystąpić dwa rodzaje problemów:
- Błędy na poziomie HTTP (4xx, 5xx): serwer odpowiedział, ale odpowiedź jest błędem. Mówi o tym
response.status_code. - Problemy na poziomie sieci: przekroczony czas, błędy DNS, nieosiągalne hosty. Te zgłaszają wyjątki.
Idiomatyczny wzorzec łączy jedno i drugie:
raise_for_status() nic nie robi przy odpowiedziach 2xx, a w pozostałych przypadkach zgłasza wyjątek. RequestException to klasa bazowa każdego błędu zgłaszanego przez requests, więc jeden blok łapie "wszystko, co poszło nie tak w rozmowie z tym endpointem".
Pobieranie pliku
Przy dużych plikach binarnych pobieraj odpowiedź strumieniowo, żeby nie trzymać jej w pamięci:
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 mówi requests, żeby nie wczytywało treści z góry. iter_content(chunk_size=...) oddaje treść kawałek po kawałku, a ty od razu zapisujesz ją na dysk.
Sesje: ponowne użycie połączeń i ustawień
Jeśli masz wysłać kilka żądań do tej samej usługi, użyj Session. Sesja ponownie wykorzystuje połączenie TCP (szybciej) i pozwala raz ustawić wartości domyślne:
import requests
session = requests.Session()
session.headers.update({"Authorization": "Bearer abc123"})
# Każde żądanie w tej sesji niesie ten nagłówek.
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"})
W skryptach, które odpytują to samo API dziesiątki razy, sesja daje wyraźne przyspieszenie.
Realistyczny przykład: mały klient GitHuba
Pobranie najnowszego wydania repozytorium:
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)
Niecałe piętnaście linii: zbuduj adres, wyślij żądanie, sprawdź błędy, wyciągnij pola, które cię interesują. Tak wygląda większość klientów API, które napiszesz.
A co z urllib?
urllib.request z biblioteki standardowej potrafi wszystko to, co requests, tylko w większej liczbie linii i mniej wygodnie. Jeśli absolutnie nie możesz dodać zależności, masz go pod ręką:
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"])
Przy wszystkim, co wykracza poza szybki skrypt, requests (albo httpx, jeśli potrzebujesz async) jest wart instalacji.
Kilka nawyków
- Zawsze ustawiaj timeout. Bez wyjątków.
- Używaj
raise_for_status()w skryptach, które oczekują sukcesu: zamienia złą odpowiedź w głośny wyjątek. - Przekazuj słowniki do
params=ijson=, a nie ręcznie sklejane napisy. - Opakowuj powiązane wywołania w
Session, gdy wiele razy odpytujesz to samo API. - Loguj
response.status_codeiresponse.textprzy debugowaniu: treść odpowiedzi zwykle mówi dokładnie, co nie spodobało się serwerowi.
Dalej: daty i godziny
Z requests w zestawie narzędzi możesz rozmawiać z każdym współczesnym webowym API, pobierać pliki i budować małe integracje między usługami. Razem z wcześniejszymi stronami o JSON i CSV masz już pełny cykl "pobierz dane, odczytaj je, zrób z nimi coś, zapisz z powrotem", czyli kształt ogromnej liczby prawdziwych skryptów w Pythonie. Większość tych danych ma jednak znacznik czasu, a następna strona opisuje, jak Python reprezentuje daty i godziny oraz jakich pułapek ze strefami czasowymi warto unikać.
Najczęściej zadawane pytania
Jak wysłać żądanie HTTP w Pythonie?
Zainstaluj bibliotekę requests poleceniem pip install requests, a potem wywołaj requests.get(url) dla GET albo requests.post(url, json=...) dla POST. Obiekt odpowiedzi ma .status_code, .text, .json() i .headers. Przykład: r = requests.get('https://api.example.com/users/1').
requests czy urllib?
requests do wszystkiego, co piszesz ręcznie, bo jego API jest nieporównanie przyjaźniejsze. urllib jest wbudowany i sprawdza się, gdy dodanie zależności jest niemożliwe, ale wymaga więcej kodu przy danych JSON czy sesjach. W produkcji wiele zespołów używa też httpx (biblioteki zgodnej z requests, obsługującej async).
Jak wysłać JSON w żądaniu POST w Pythonie?
Przekaż json={'key': 'value'} do requests.post(...). requests serializuje słownik do JSON i sam ustawia Content-Type: application/json. Nie przekazuj jednocześnie data= i json=: wybierz jedno.
Jak obsługiwać błędy w bibliotece requests?
Sprawdź response.status_code (200 oznacza sukces) albo wywołaj response.raise_for_status(), żeby zgłosić wyjątek przy kodach 4xx/5xx. Problemy na poziomie sieci (przekroczony czas, błędy DNS) zgłaszają podklasy requests.RequestException: łap ten wyjątek, żeby objąć oba przypadki.