Tutorial API filme Python
Construiești un mic instrument Python în linia de comandă care primește titlul unui film, îl caută, apoi preia detaliile, distribuția și evaluările cu un singur apel în plus. Ai nevoie de Python 3.10 sau mai nou, biblioteca requests și o cheie amodb. Durează cam 15 minute și se încheie cu un script complet.
Vei construi movie.py, un instrument în linia de comandă. Îi dai un titlu, el caută, alege prima potrivire și afișează anul, durata, genurile, distribuția principală, evaluarea IMDb și un URL de poster. Are nevoie de Python 3.10 sau mai nou, biblioteca requests și o cheie amodb. Socotește 15 minute.
Pasul 1: obține o cheie API
amodb este plătit în avans și fără nivel gratuit, deci alege mai întâi un plan la prețuri. Planurile de 20, 40 și 120 de cereri pe minut întorc toate aceleași date. Apoi creează o cheie în panoul contului tău. O cheie are 32 de caractere hexazecimale minuscule.
Exportă-o ca variabilă de mediu, ca să rămână în afara codului sursă:
În Windows PowerShell aceeași linie este $env:AMODB_.
export AMODB_KEY="YOUR_API_KEY"Pasul 2: configurează proiectul
Creează un mediu virtual și instalează requests:
În Windows rulează .venv\Scripts\activate în loc de a doua linie.
python -m venv .venv
source .venv/bin/activate
pip install requestsPasul 3: trimite prima cerere
Endpoint-ul de căutare este GET /3/search/movie cu un parametru query. Acest fragment afișează starea HTTP și ID-ul celei mai bune potriviri. Transmite mereu un timeout, pentru că requests așteaptă la nesfârșit implicit.
Răspunsul are structura TMDB: un array results sortat după relevanță, plus page și total_results. Vezi căutare pentru fiecare filtru.
import os
import requests
res = requests.get(
"https://api.amodb.dev/3/search/movie",
params={"query": "fight club", "language": "en-US"},
headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
timeout=10,
)
print(res.status_code, res.json()["results"][0]["id"])Pasul 4: preia detalii, distribuție și evaluări
Rezultatele căutării sunt scurte. Folosește ID-ul cu GET / și adaugă append_ ca să primești distribuția și scorurile IMDb, Kinopoisk, Metacritic, Rotten Tomatoes și TMDB în aceeași cerere. Fiecare scor este un obiect cu value și votes, sau null când sursa nu se cunoaște, deci testează înainte să afișezi. Detaliile sunt pe paginile filme și evaluări. În scriptul final session este un requests.Session care poartă deja antetul Authorization.
params = {"language": "en-US", "append_to_response": "credits,ratings"}
movie = session.get(f"{BASE}/movie/{movie_id}", params=params, timeout=10).json()
imdb = (movie.get("ratings") or {}).get("imdb")Pasul 5: tratează erorile și limitele de cereri
Verifică starea înainte să parsezi corpul. Erorile folosesc corpul TMDB cu status_code și status_message.
| Stare | Semnificație | Ce face scriptul |
|---|---|---|
| 401 | Cheie invalidă sau suspendată | Se oprește și spune să verifici AMODB_KEY. O reîncercare nu ajută. |
| 404 | Niciun astfel de ID | Afișează status_message. |
| 429 | Ai depășit cererile pe minut | Doarme Retry-After secunde (plafon 30), apoi reîncearcă de până la două ori. |
| 504 | Timeout la backend | Afișat ca eroare. Rulează comanda din nou. |
| Eșec de rețea | Fără conexiune sau timeout | Prins ca requests.RequestException și raportat. |
Bucla de reîncercare este scurtă:
Mai multe coduri sunt listate la erori, iar regulile ferestrei sunt la limite de cereri.
for attempt in range(3):
res = session.get(url, params=params, timeout=10)
if res.status_code != 429 or attempt == 2:
break
time.sleep(min(int(res.headers.get("Retry-After", "1")), 30))Pasul 6: păstrează răspunsurile în cache pe disc
Marginea ține detaliile 6 ore și căutarea 10 minute. Propriul tău cache economisește drumul dus-întors și nu consumă nicio cerere din limita ta. Scriptul final stochează fiecare răspuns într-un fișier denumit după un hash al căii și parametrilor și reutilizează fișierul cât timp este mai tânăr decât TTL.
Pasul 7: programul complet
Salvează asta ca movie.py și rulează python movie.py "fight club".
import hashlib
import json
import os
import sys
import time
from pathlib import Path
import requests
BASE = "https://api.amodb.dev/3"
POSTER = "https://img.amodb.dev/t/p/w342"
CACHE_DIR = Path.home() / ".cache" / "amodb-movie-cli"
SEARCH_TTL = 10 * 60
DETAILS_TTL = 6 * 60 * 60
class ApiError(Exception):
pass
def error_message(res):
try:
return res.json()["status_message"]
except (ValueError, KeyError):
return res.reason
def get(session, path, params, ttl):
CACHE_DIR.mkdir(parents=True, exist_ok=True)
stamp = f"{path}?{json.dumps(params, sort_keys=True)}"
cache_file = CACHE_DIR / (hashlib.sha256(stamp.encode()).hexdigest() + ".json")
if cache_file.exists() and time.time() - cache_file.stat().st_mtime < ttl:
return json.loads(cache_file.read_text(encoding="utf-8"))
for attempt in range(3):
try:
res = session.get(BASE + path, params=params, timeout=10)
except requests.RequestException as exc:
raise ApiError(f"network error: {exc}") from exc
if res.status_code != 429 or attempt == 2:
break
time.sleep(min(int(res.headers.get("Retry-After", "1")), 30))
if res.status_code == 401:
raise ApiError("401: the key is invalid or suspended. Check AMODB_KEY.")
if res.status_code != 200:
raise ApiError(f"{res.status_code}: {error_message(res)}")
cache_file.write_text(res.text, encoding="utf-8")
return res.json()
def search_movie(session, title):
data = get(session, "/search/movie", {"query": title, "language": "en-US"}, SEARCH_TTL)
if not data["results"]:
raise ApiError(f"no movie found for {title!r}")
return data["results"][0]["id"]
def movie_details(session, movie_id):
params = {"language": "en-US", "append_to_response": "credits,ratings"}
return get(session, f"/movie/{movie_id}", params, DETAILS_TTL)
def print_card(movie):
year = (movie.get("release_date") or "")[:4] or "unknown year"
genres = ", ".join(g["name"] for g in movie["genres"])
cast = ", ".join(p["name"] for p in movie["credits"]["cast"][:3])
imdb = (movie.get("ratings") or {}).get("imdb")
print(f"{movie['title']} ({year}), {movie.get('runtime') or '?'} min")
print(f"Genres: {genres or 'none listed'}")
print(f"Cast: {cast or 'none listed'}")
print(f"IMDb: {imdb['value']} ({imdb['votes']} votes)" if imdb else "IMDb: not known")
if movie.get("poster_path"):
print(f"Poster: {POSTER}{movie['poster_path']}")
print(movie.get("overview") or "No overview in this language.")
def main():
if len(sys.argv) < 2:
sys.exit('Usage: python movie.py "fight club"')
key = os.environ.get("AMODB_KEY")
if not key:
sys.exit("Set AMODB_KEY first.")
session = requests.Session()
session.headers["Authorization"] = f"Bearer {key}"
try:
movie_id = search_movie(session, " ".join(sys.argv[1:]))
print_card(movie_details(session, movie_id))
except ApiError as exc:
sys.exit(f"error: {exc}")
if __name__ == "__main__":
main()Pașii următori
- Afișează un poster într-o altă dimensiune. Gazda de imagini acceptă oricare dintre dimensiunile listate la imagini și nu cere cheie.
- Adaugă un filtru
yearla apelul de căutare sau treci la descoperire pentru liste filtrate. - Arată un indicator de atmosferă lângă evaluare adăugând
moodslaappend_to_response. Fiecare intrare dinmoods.resultsare unnameși unavgpe o scară de la 0 la 10. - Citește ghidul de pornire rapidă pentru același flux în curl, apoi încearcă tutorialul JavaScript pentru o variantă în lot.