Падручнік па Python і API фільмаў
Створыце невялікую кансольную ўтыліту на Python: яна прымае назву фільма, шукае яго, а потым адным дадатковым выклікам атрымлівае дэталі, акцёраў і рэйтынгі. Патрэбныя Python 3.10 або новей, бібліятэка requests і ключ amodb. Гэта зойме каля 15 хвілін, у выніку атрымаецца поўны скрыпт.
Вы створыце movie.py, кансольную ўтыліту. Вы перадаяце ёй назву, яна шукае, бярэ першае супадзенне і друкуе год, працягласць, жанры, галоўных акцёраў, рэйтынг IMDb і URL постэра. Патрэбныя Python 3.10 або новей, бібліятэка requests і ключ amodb. Закладзіце 15 хвілін.
Крок 1: Атрымайце ключ API
amodb працуе з аплатай наперад і без бясплатнага тарыфа, таму спачатку абярыце план на старонцы цэны. Планы на 20, 40 і 120 запытаў у хвіліну вяртаюць аднолькавыя даныя. Потым стварыце ключ у асабістым кабінеце. Ключ уяўляе сабой 32 шаснаццатковыя сімвалы ў ніжнім рэгістры.
Экспартуйце яго як зменную асяроддзя, каб ён не трапіў у зыходны код:
У Windows PowerShell той жа радок выглядае так: $env:AMODB_.
export AMODB_KEY="YOUR_API_KEY"Крок 2: Падрыхтуйце праект
Стварыце віртуальнае асяроддзе і ўсталюйце requests:
У Windows замест другога радка выканайце .venv\Scripts\activate.
python -m venv .venv
source .venv/bin/activate
pip install requestsКрок 3: Адпраўце першы запыт
Эндпоінт пошуку: GET /3/search/movie з параметрам query. Гэты фрагмент друкуе HTTP-статус і id найлепшага супадзення. Заўсёды перадавайце тайм-аўт, бо requests па змаўчанні чакае бясконца.
Адказ мае форму TMDB: масіў results, адсартаваны па рэлевантнасці, а таксама page і total_results. Усе фільтры гл. на старонцы пошук.
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"])Крок 4: Атрымайце дэталі, акцёраў і рэйтынгі
Вынікі пошуку кароткія. Скарыстайцеся id у GET / і дадайце append_, каб у тым жа запыце атрымаць акцёраў і ацэнкі IMDb, Kinopoisk, Metacritic, Rotten Tomatoes і TMDB. Кожная ацэнка ўяўляе сабой аб’ект з value і votes або null, калі крыніца невядомая, таму правярайце перад друкам. Падрабязнасці на старонках фільмы і рэйтынгі. У канчатковым скрыпце session гэта requests.Session, які ўжо нясе загаловак 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")Крок 5: Апрацоўка памылак і лімітаў
Правярайце статус, перш чым разбіраць цела. Памылкі маюць цела TMDB з status_code і status_message.
| Статус | Значэнне | Што робіць скрыпт |
|---|---|---|
| 401 | Недапушчальны або прыпынены ключ | Спыняецца і прапануе праверыць AMODB_KEY. Паўтор не дапаможа. |
| 404 | Такога id няма | Друкуе status_message. |
| 429 | Перавышаны ліміт запытаў у хвіліну | Чакае Retry-After секунд (не больш за 30), потым паўтарае да двух разоў. |
| 504 | Тайм-аўт бэкенда | Выводзіцца як памылка. Запусціце каманду яшчэ раз. |
| Збой сеткі | Няма злучэння або тайм-аўт | Перахопліваецца як requests.RequestException і паведамляецца. |
Цыкл паўтораў кароткі:
Іншыя коды пералічаны на старонцы памылкі, а правілы акна на старонцы ліміты запытаў.
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))Крок 6: Кэшуйце адказы на дыску
Edge трымае дэталі 6 гадзін, а пошук 10 хвілін. Уласны кэш пазбаўляе ад сеткавага выкліку і не расходуе запыты з вашага ліміту. Канчатковы скрыпт захоўвае кожны адказ у файле, імя якога ўтвараецца з хэша шляху і параметраў, і выкарыстоўвае файл, пакуль ён маладзейшы за TTL.
Крок 7: Поўная праграма
Захавайце гэта як movie.py і запусціце 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()Наступныя крокі
- Раздрукуйце постэр іншага памеру. Хост выяў прымае любы з памераў са старонкі выявы і не патрабуе ключа.
- Дадайце фільтр
yearу выклік пошуку або перайдзіце да пошуку па фільтрах для спісаў з фільтрамі. - Паказвайце індыкатар настрою побач з рэйтынгам, дадаўшы
moodsуappend_to_response. Кожны элемент уmoods.resultsмаеnameіavgпа шкале ад 0 да 10. - Прачытайце хуткі старт пра той жа працэс у curl, потым паспрабуйце падручнік па JavaScript з пакетнай версіяй.