Witaj ponownie, @name! Darwin tutaj.
W poprzedniej lekcji nauczyłeś/aś się formatów danych - jak przechowywać informacje (JSON, CSV, XML, YAML). Teraz czas na HTTP - protokół, który pozwala przesyłać te dane przez internet!
Analogia Safari: Formaty danych to język, w którym zapisujesz obserwacje. HTTP to system kurierski - sposób, w jaki wysyłasz te obserwacje między stacjami badawczymi w różnych częściach Afryki! 📦🌍
HTTP (HyperText Transfer Protocol) to protokół komunikacyjny - zbiór zasad, według których komputery wymieniają się danymi przez internet.
Jak to działa:
1[KLIENT] ---(HTTP REQUEST)---> [SERWER]
2 ↓ ↓
3 ↓ Przetwarza
4 ↓ ↓
5 ↓ <---(HTTP RESPONSE)--- [SERWER]Przykład Safari: Wysyłasz prośbę do centralnej bazy danych w Nairobi: "Podaj mi dane o lwach w Serengeti". Serwer odpowiada: "Oto 120 lwów w formie JSON"!
HTTP ma różne metody (czasem nazywane "czasownikami") - określają CO chcesz zrobić:
| Metoda | Przeznaczenie | Analogia Safari | |--------|---------------|-----------------| | GET | Pobierz dane | "Pokaż mi listę gatunków" 📖 | | POST | Utwórz nowy zasób | "Dodaj nową obserwację słonia" ➕ | | PUT | Zaktualizuj cały zasób | "Zastąp cały profil lwa nowymi danymi" 🔄 | | PATCH | Zaktualizuj część zasobu | "Zmień tylko populację goryla" ✏️ | | DELETE | Usuń zasób | "Usuń błędną obserwację" 🗑️ |
Najważniejsze: GET i POST - używane w 90% przypadków!
1GET /api/species/lion HTTP/1.1
2Host: safari-api.comCechy:
?name=lion&habitat=savanna)1POST /api/observations HTTP/1.1
2Host: safari-api.com
3Content-Type: application/json
4
5{
6 "species": "Panthera leo",
7 "count": 12,
8 "location": "Serengeti"
9}Cechy:
Serwer zawsze odpowiada kodem statusu - liczbą mówiącą czy operacja się powiodła:
Analogia Safari: Kod 200 = "Znalazłem lwa!", 404 = "Nie ma tu żadnego lwa", 500 = "Mój radiotelefon się zepsuł" 📻
to najpopularniejsza biblioteka Python do HTTP - prosta, elegancka, potężna!requests
1pip install requests1import requests
2
3# Pobierz dane
4response = requests.get("https://api.github.com/users/octocat")
5
6# Status code
7print(response.status_code) # 200
8
9# Dane jako JSON
10data = response.json()
11print(data["login"]) # "octocat"
12print(data["name"]) # "The Octocat"
13
14# Dane jako tekst
15print(response.text) # Surowy JSON jako string
16
17# Nagłówki odpowiedzi
18print(response.headers["Content-Type"]) # "application/json"1import requests
2
3response = requests.get("https://api.github.com/users/nonexistent")
4
5# Metoda 1: Sprawdź status code
6if response.status_code == 200:
7 print("Sukces!")
8elif response.status_code == 404:
9 print("Nie znaleziono")
10
11# Metoda 2: Użyj response.ok (True dla 2xx)
12if response.ok:
13 data = response.json()
14 print(data)
15else:
16 print(f"Błąd: {response.status_code}")
17
18# Metoda 3: Rzuć wyjątek przy błędzie
19try:
20 response.raise_for_status() # Rzuca HTTPError dla 4xx/5xx
21 data = response.json()
22except requests.HTTPError as e:
23 print(f"HTTP Error: {e}")GET requests często używają parametrów w URL:
1import requests
2
3# Ręcznie w URL
4response = requests.get("https://api.example.com/species?habitat=savanna&endangered=true")
5
6# Lepiej: słownik params (automatyczne kodowanie)
7params = {
8 "habitat": "savanna",
9 "endangered": "true",
10 "min_population": 100
11}
12
13response = requests.get("https://api.example.com/species", params=params)
14# URL: https://api.example.com/species?habitat=savanna&endangered=true&min_population=100
15
16print(response.url) # Zobacz wygenerowany URLNagłówki to metadane zapytania - informacje o kliencie, oczekiwanym formacie, autoryzacji:
1import requests
2
3# Własne nagłówki
4headers = {
5 "User-Agent": "Safari-Data-Collector/1.0",
6 "Accept": "application/json",
7 "Authorization": "Bearer YOUR_API_KEY_HERE"
8}
9
10response = requests.get("https://api.example.com/species", headers=headers)
11
12# Sprawdź nagłówki odpowiedzi
13print(response.headers["Content-Type"])
14print(response.headers["Date"])Popularne nagłówki:
User-Agent - identyfikacja klientaAccept - oczekiwany format odpowiedziContent-Type - format wysyłanych danychAuthorization - token/klucz API1import requests
2
3# Dane do wysłania
4new_observation = {
5 "species": "Panthera leo",
6 "location": "Serengeti",
7 "count": 12,
8 "date": "2024-01-15",
9 "observer": "Darwin"
10}
11
12# POST z JSON
13response = requests.post(
14 "https://api.safari.com/observations",
15 json=new_observation # Automatycznie: Content-Type: application/json
16)
17
18if response.status_code == 201: # Created
19 created_data = response.json()
20 print(f"Utworzono obserwację ID: {created_data['id']}")
21else:
22 print(f"Błąd: {response.status_code}")1import requests
2
3# Dane formularza
4form_data = {
5 "species": "Loxodonta africana",
6 "count": "35"
7}
8
9response = requests.post(
10 "https://api.safari.com/quick-report",
11 data=form_data # Content-Type: application/x-www-form-urlencoded
12)1import requests
2
3# PUT - zastąp cały zasób
4updated_species = {
5 "scientific_name": "Panthera leo",
6 "population": 125, # Zaktualizowana
7 "habitat": "sawanna"
8}
9
10response = requests.put(
11 "https://api.safari.com/species/lion",
12 json=updated_species
13)
14
15# PATCH - zmień tylko część
16partial_update = {
17 "population": 125 # Tylko populacja
18}
19
20response = requests.patch(
21 "https://api.safari.com/species/lion",
22 json=partial_update
23)
24
25# DELETE - usuń zasób
26response = requests.delete("https://api.safari.com/observations/12345")
27
28if response.status_code == 204: # No Content
29 print("Usunięto pomyślnie")1import requests
2from requests.exceptions import RequestException, Timeout, ConnectionError
3
4def fetch_species_data(species_id: str):
5 """Pobierz dane gatunku z obsługą błędów"""
6 try:
7 response = requests.get(
8 f"https://api.safari.com/species/{species_id}",
9 timeout=5 # Timeout po 5 sekundach
10 )
11
12 # Sprawdź status
13 response.raise_for_status()
14
15 # Przetwórz dane
16 return response.json()
17
18 except Timeout:
19 print("⏱️ Timeout - serwer nie odpowiedział w czasie")
20 return None
21
22 except ConnectionError:
23 print("🔌 Błąd połączenia - sprawdź internet")
24 return None
25
26 except requests.HTTPError as e:
27 print(f"❌ HTTP Error {response.status_code}: {e}")
28 return None
29
30 except RequestException as e:
31 print(f"⚠️ Błąd zapytania: {e}")
32 return None
33
34# Użycie
35data = fetch_species_data("lion")
36if data:
37 print(f"Gatunek: {data['name']}")1import requests
2from typing import List, Dict, Optional
3from datetime import datetime
4
5class SafariAPIClient:
6 """Klient API do systemu obserwacji Safari"""
7
8 def __init__(self, base_url: str, api_key: str):
9 self.base_url = base_url.rstrip("/")
10 self.api_key = api_key
11 self.session = requests.Session() # Reużywalne połączenie
12
13 # Domyślne nagłówki dla wszystkich zapytań
14 self.session.headers.update({
15 "User-Agent": "Safari-Data-Collector/2.0",
16 "Authorization": f"Bearer {self.api_key}",
17 "Accept": "application/json"
18 })
19
20 def get_species(self, species_id: str) -> Optional[Dict]:
21 """Pobierz dane gatunku"""
22 try:
23 response = self.session.get(
24 f"{self.base_url}/species/{species_id}",
25 timeout=10
26 )
27 response.raise_for_status()
28 return response.json()
29
30 except requests.RequestException as e:
31 print(f"❌ Błąd pobierania gatunku: {e}")
32 return None
33
34 def search_species(self, habitat: Optional[str] = None,
35 endangered: Optional[bool] = None,
36 min_population: Optional[int] = None) -> List[Dict]:
37 """Wyszukaj gatunki z filtrami"""
38 params = {}
39 if habitat:
40 params["habitat"] = habitat
41 if endangered is not None:
42 params["endangered"] = str(endangered).lower()
43 if min_population:
44 params["min_population"] = min_population
45
46 try:
47 response = self.session.get(
48 f"{self.base_url}/species",
49 params=params,
50 timeout=10
51 )
52 response.raise_for_status()
53 return response.json()
54
55 except requests.RequestException as e:
56 print(f"❌ Błąd wyszukiwania: {e}")
57 return []
58
59 def create_observation(self, species: str, location: str,
60 count: int, notes: str = "") -> Optional[Dict]:
61 """Utwórz nową obserwację"""
62 observation_data = {
63 "species": species,
64 "location": location,
65 "count": count,
66 "date": datetime.now().isoformat(),
67 "notes": notes
68 }
69
70 try:
71 response = self.session.post(
72 f"{self.base_url}/observations",
73 json=observation_data,
74 timeout=10
75 )
76 response.raise_for_status()
77
78 if response.status_code == 201:
79 created = response.json()
80 print(f"✓ Utworzono obserwację ID: {created.get('id')}")
81 return created
82 else:
83 return response.json()
84
85 except requests.RequestException as e:
86 print(f"❌ Błąd tworzenia obserwacji: {e}")
87 return None
88
89 def update_species_population(self, species_id: str,
90 new_population: int) -> bool:
91 """Zaktualizuj populację gatunku (PATCH)"""
92 try:
93 response = self.session.patch(
94 f"{self.base_url}/species/{species_id}",
95 json={"population": new_population},
96 timeout=10
97 )
98 response.raise_for_status()
99 print(f"✓ Zaktualizowano populację {species_id} → {new_population}")
100 return True
101
102 except requests.RequestException as e:
103 print(f"❌ Błąd aktualizacji: {e}")
104 return False
105
106 def delete_observation(self, observation_id: str) -> bool:
107 """Usuń obserwację"""
108 try:
109 response = self.session.delete(
110 f"{self.base_url}/observations/{observation_id}",
111 timeout=10
112 )
113 response.raise_for_status()
114
115 if response.status_code == 204:
116 print(f"✓ Usunięto obserwację {observation_id}")
117 return True
118
119 return False
120
121 except requests.RequestException as e:
122 print(f"❌ Błąd usuwania: {e}")
123 return False
124
125 def get_statistics(self) -> Optional[Dict]:
126 """Pobierz statystyki (z parametrami query)"""
127 params = {
128 "include_endangered": "true",
129 "group_by": "habitat"
130 }
131
132 try:
133 response = self.session.get(
134 f"{self.base_url}/statistics",
135 params=params,
136 timeout=15
137 )
138 response.raise_for_status()
139 return response.json()
140
141 except requests.RequestException as e:
142 print(f"❌ Błąd pobierania statystyk: {e}")
143 return None
144
145 def close(self):
146 """Zamknij sesję"""
147 self.session.close()
148
149
150# === DEMONSTRACJA ===
151
152print("=== SAFARI API CLIENT ===\n")
153
154# Inicjalizacja klienta
155client = SafariAPIClient(
156 base_url="https://api.safari-data.org/v1",
157 api_key="demo_key_12345"
158)
159
160# GET - pobierz gatunek
161print("1. Pobieranie danych lwa...")
162lion_data = client.get_species("lion")
163if lion_data:
164 print(f" Gatunek: {lion_data.get('common_name')}")
165 print(f" Populacja: {lion_data.get('population')}")
166
167# GET z parametrami - wyszukiwanie
168print("\n2. Wyszukiwanie zagrożonych gatunków w sawannie...")
169endangered = client.search_species(habitat="savanna", endangered=True)
170print(f" Znaleziono {len(endangered)} gatunków")
171
172# POST - tworzenie obserwacji
173print("\n3. Tworzenie nowej obserwacji...")
174new_obs = client.create_observation(
175 species="Panthera leo",
176 location="Serengeti North",
177 count=8,
178 notes="Pride with 2 cubs"
179)
180
181# PATCH - aktualizacja
182print("\n4. Aktualizacja populacji...")
183client.update_species_population("lion", 125)
184
185# DELETE - usuwanie
186print("\n5. Usuwanie błędnej obserwacji...")
187client.delete_observation("obs_12345")
188
189# GET ze statystykami
190print("\n6. Pobieranie statystyk...")
191stats = client.get_statistics()
192if stats:
193 print(f" Łączna liczba gatunków: {stats.get('total_species')}")
194
195# Zamknij sesję
196client.close()
197print("\n✓ Sesja zakończona")Jeśli robisz wiele zapytań do tego samego API, użyj
Session - reużywa połączenie TCP:1import requests
2
3# BEZ Session - każde zapytanie = nowe połączenie
4for i in range(100):
5 response = requests.get("https://api.example.com/data")
6
7# Z Session - jedno połączenie, szybsze!
8session = requests.Session()
9session.headers.update({"Authorization": "Bearer token123"})
10
11for i in range(100):
12 response = session.get("https://api.example.com/data")
13
14session.close()Zawsze używaj timeout, aby zapytanie nie wisiało w nieskończoność:
1import requests
2
3# Timeout 5 sekund
4response = requests.get("https://slow-api.com/data", timeout=5)
5
6# Różne timeouty: (connect_timeout, read_timeout)
7response = requests.get("https://api.com/data", timeout=(3, 10))
8# 3s na połączenie, 10s na odczyt danychW tej lekcji nauczyłeś/aś się:
requests: requests.get(), requests.post()params=), nagłówki (headers=)json=) i form data (data=)response.raise_for_status(), try/exceptPrzed przejściem dalej:
Analogia Safari: HTTP to system kurierski między stacjami badawczymi. GET = "wyślij mi dane", POST = "zapisz te nowe obserwacje", kody statusu = potwierdzenia dostawy! 📦✉️
W następnej lekcji Darwin pokaże Ci REST API - standardy architektury API, które pozwalają budować profesjonalne systemy wymiany danych! 🏗️🌐