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.

.py Python Actualizat Vezi ca Markdown

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_KEY = "YOUR_API_KEY".

Shell
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.

Shell
python -m venv .venv
source .venv/bin/activate
pip install requests

Pasul 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.

Python
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 /3/movie/{movie_id} și adaugă append_to_response=credits,ratings 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.

Python
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.

StareSemnificațieCe face scriptul
401Cheie invalidă sau suspendatăSe oprește și spune să verifici AMODB_KEY. O reîncercare nu ajută.
404Niciun astfel de IDAfișează status_message.
429Ai depășit cererile pe minutDoarme Retry-After secunde (plafon 30), apoi reîncearcă de până la două ori.
504Timeout la backendAfișat ca eroare. Rulează comanda din nou.
Eșec de rețeaFără conexiune sau timeoutPrins 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.

Python
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".

Python
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 year la apelul de căutare sau treci la descoperire pentru liste filtrate.
  • Arată un indicator de atmosferă lângă evaluare adăugând moods la append_to_response. Fiecare intrare din moods.results are un name și un avg pe 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.