Скачивание видео из соцсетей на Python
Один GET, один формат JSON, 1.5 кредита за успешный разбор. Здесь про то, что не видно из самого ответа: как приходят смешанные посты, когда выбор рендишена есть, а когда его нет, какие ошибки заслуживают второй попытки и как получить файл, не загружая его в память.
Обновлено
Первый вызов
Одного аргумента timeout уже достаточно, чтобы выбрать здесь httpx: подвисший источник должен уронить ваш запрос, а не держать его. Тридцать секунд — разумный потолок, собственный бюджет шлюза на источник ниже.
Pythonimport httpx
r = httpx.get(
"https://api.saveapi.org/v1/download",
params={"url": "https://www.instagram.com/reel/Cx1a2b3c/"},
headers={"Authorization": "Bearer sk_live_xxxx"},
timeout=30,
)
data = r.json()Читайте medias[], а не угадывайте
В ответе всегда один и тот же массив — было в посте одно видео или десять картинок. В смешанных постах типы сохраняются в порядке поста: карусель из двух фото и клипа даст две записи image и одну video. Ветвитесь по type — и один и тот же код закроет все платформы.
Pythonfor media in data["medias"]:
if media["type"] == "video":
print("video:", media["url"])
elif media["type"] == "image":
print("image:", media["url"])
elif media["type"] == "audio":
print("track:", media["url"])Где есть выбор, выберите рендишен
Некоторые источники публикуют несколько рендишенов, и в таких ответах есть formats[] — в каждом готовая ссылка и точный размер в мегабайтах. Ни второго запроса, ни параметра формата: отфильтруйте список на своей стороне и возьмите самый крупный файл, который влезает в ваш потолок.
Pythonbest = None
for fmt in data.get("formats", []):
if fmt["size_mb"] <= 45 and (best is None or fmt["size_mb"] > best["size_mb"]):
best = fmt
url = best["url"] if best else data["medias"][0]["url"]Повторяйте только то, что лечится повтором
Таймаут или ошибка источника стоят ещё одной попытки. Закрытый пост, удалённый пост или неподдерживаемый домен — нет: второй вызов вернёт тот же ответ, а на тарифицируемых ошибках потратит второй кредит. Разложите коды на два набора и принимайте решение по набору, а не по тексту сообщения.
PythonRETRY = {"UPSTREAM_TIMEOUT", "UPSTREAM_ERROR"}
def download(link: str, attempts: int = 2) -> dict:
for attempt in range(attempts):
data = httpx.get(API, params={"url": link}, headers=HEAD, timeout=30).json()
if data["success"]:
return data
if data["error"]["code"] not in RETRY:
raise RuntimeError(data["error"]["message"]) # qayta urinish yordam bermaydi
raise RuntimeError("source unavailable")Следите за заголовками лимита
В каждом ответе есть минутный лимит ключа и его остаток, а на платных ключах ещё и остаток кредитов в пакете. Читайте их из ответа, который у вас уже есть, вместо опроса баланса по таймеру.
Pythonprint(r.headers["X-RateLimit-Limit"]) # kalitning daqiqadagi chegarasi
print(r.headers["X-RateLimit-Remaining"]) # shu daqiqada qolgani
print(r.headers.get("X-Credits-Remaining")) # paketdagi qolgan kreditБерите файл потоком, а не в память
Полученный адрес указывает прямо на CDN источника, то есть байты не проходят через API. Пишите его на диск потоком: 4K-ролик в памяти — самый быстрый способ получить убитый контейнер.
Pythonwith httpx.stream("GET", data["medias"][0]["url"]) as source:
with open("reel.mp4", "wb") as out:
for chunk in source.iter_bytes():
out.write(chunk)Вопросы перед началом
- Есть ли официальный SDK для Python?
- Нет, и одному эндпоинту он не особо нужен. Весь клиент — это вызов httpx с заголовком Bearer; обёртка в восемь строк даст вам типизацию и повторы без зависимости, которую надо обновлять.
- Проходит ли файл через API?
- Нет. Вы получаете адрес, указывающий на CDN источника, и забираете байты напрямую оттуда — поэтому скорость скачивания зависит от этого CDN, а не от нас.
- Как узнать, во сколько обошёлся вызов?
- В ответе есть credits.spent и credits.remaining. Успешный разбор — 1.5 кредита (история Instagram 5, Rutube 3). Закрытый или отсутствующий пост стоит 0.1, а сбой на нашей стороне не стоит ничего.
Платформы, использованные здесь
Другие руководства
Запустите это руководство со своим ключом
Ключ за минуту. 1 000 бесплатных кредитов, без карты и без срока.