Добавить в корзинуПозвонить
Найти в Дзене
DinoWithPython

Pytube скачиваем видео с YouTube

Данная статья актуальна на 11.05.2022 г. и возможно позже, если Youtube не обновлялся. К сожалению, существует уже очень давняя проблема с "content-lengh". Некоторые видео невозможно скачать. Как я понял, разрабы несколько забросили своё творение и поддерживают постольку-поскольку, но в целом им можно пользоваться. Сейчас для скачивание таких проблемных видео я использую "yt-dlp". Возможно, позже напишу статейку и для него. Задача: скачать видео в максимальном качестве + превью. Устанавливаем модуль pytube: pip install pytube Небольшое, но очень важное отступление. Для видео в формате 1080p (или выше) звуковая дорожка идет отдельно от видео файла. Поэтому если Вас устроит 720p и 30fps, то всё проще, если же нет, скорее всего придется дополнительно воспользоваться различными программами для объединения видеодорожки высокого качества со звуковой дорожкой. Об этом упоминается в официальной документации модуля putybe. Если Вам интересно, то перейдите по ссылке. Авторы pytube рекомендуют ис

Данная статья актуальна на 11.05.2022 г. и возможно позже, если Youtube не обновлялся. К сожалению, существует уже очень давняя проблема с "content-lengh". Некоторые видео невозможно скачать. Как я понял, разрабы несколько забросили своё творение и поддерживают постольку-поскольку, но в целом им можно пользоваться. Сейчас для скачивание таких проблемных видео я использую "yt-dlp". Возможно, позже напишу статейку и для него.

Задача: скачать видео в максимальном качестве + превью.

Устанавливаем модуль pytube:

pip install pytube

Небольшое, но очень важное отступление. Для видео в формате 1080p (или выше) звуковая дорожка идет отдельно от видео файла. Поэтому если Вас устроит 720p и 30fps, то всё проще, если же нет, скорее всего придется дополнительно воспользоваться различными программами для объединения видеодорожки высокого качества со звуковой дорожкой. Об этом упоминается в официальной документации модуля putybe. Если Вам интересно, то перейдите по ссылке.

Авторы pytube рекомендуют использовать модуль ffmpeg. В моём решении FFmpeg используется в коде питона, можете обратиться к официальной документации и использовать его отдельно. Переходим на сайт, скачиваем "FFmpeg" под вашу ОС и запихиваем в папку "Scripts" виртуального окружения, либо, туда, где у вас установлен питон, потому что модуль ffmpeg является лишь оболочкой и без "exe" работать не будет(в моём случае использовался windows 10). Скачать можно по официальной ссылке FFmpeg.

Официальный сайт модуля FFmpeg
Официальный сайт модуля FFmpeg

Поместили в папочку с питоном:

Папка Scripts м виртуальном окружении
Папка Scripts м виртуальном окружении

А теперь установим FFmpeg при помощи старого доброго pip:

pip install ffmpeg

Подготовительные операции завершены и переходим к написанию кода. Допустим, что нам нужно скачать только одно видео в качестве примера. Не помешает тут и строка прогресса скачивания, поэтому дополнительно использую и её(запускаю через командную строку, в IDE может не работать).

Соответственно раздел импортов(для вашего удобства оставляю код для копипаста, а так же скрины из ide):

from pytube import YouTube
from pytube.cli import on_progress
import ffmpeg
import requests
Необходимые импорты
Необходимые импорты

Модуль requests используется для скачивания превью, но об этом дальше.

Создадим две функции:
1) для скачивания видео
download_video();
2) для объединения видео с аудио
concat_video().

Строка загрузки была любезно позаимствована на просторах интернета.

Теперь же о том, как скачать видео. В официальной документации нам доступно это:

-4

Для начала создаем объект Youtube, в него передаем ссылку на видео, используем разные методы для фильтрации и скачивания видео и можно в одну строку. Этим заканчивались многие гайды и мы получали видео в 320х240(P.S.спасибо, блин, большое).

Используем инструкцию, но с некоторыми пояснениями. Создаем строку загрузки, добавляем ей цвета и всё такое и создаем объект Youtube, в который передаем ссылку на видео:

def download_video():
fuchsia = '\033[38;2;255;00;255m' # color as hex FF00FF
reset_color = '\033[39m'
# url is url of youtube video to download.
""" Instantiates YouTube class and downloads selected video. Uses Built-in
pytube.cli function on_progress to show a DOS style progress bar. """

yt = YouTube('https://www.youtube.com/watch?v=9HZ-W2m_ElA', on_progress_callback=on_progress)

# following line displays title and number of times video has been viewed.
print(f'\n' + fuchsia + 'Downloading: ', yt.title, '~ viewed', yt.views,
'times.')
-5

Правильнее было бы создать переменную "link", в неё записывать нужные нам ссылки для скачивания(например в цикле) и уже передавать в функцию, но этот код всего лишь инструкция по работе с модулем и при желании его можно полностью переработать под свои нужды. Из всего этого кода нас интересует больше объект "yt". Используя метод "streams", посмотрим, что в нём скрывается. В качестве примера оставлю пару строк и некоторые моменты выделю жирным(print(yt.streams)):

<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">
<Stream: itag="299" mime_type="video/mp4" res="1080p" fps="60fps" vcodec="avc1.64002a" progressive="False"
type="video">
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2" progressive="False"
type="audio">

Тут и скрывается проблема. Как видите, для res="720p" fps="30fps" у нас доступная аудиодорожка acodec="mp4a.40.2", а вот для видео в res="1080p" fps="60fps" её нет, поэтому приходится дополнительно скачивать аудио. Если Вас устроит 720p, то смело фильтруем под это качество и скачиваем, можно в одну строку как по документации:

yt.streams.filter(progressive=True, file_extension='mp4').desc().first() .download()

Дополню по методам:
filter() - очевидно что фильтрует по заданным условиям, это файл в формате mp4 с атрибутом progressive(со звуком).
desc() - сортирует по разрешению видео, делая первым в списке видео в 720р, а вторым в 360р(можно так же включить order_by, но на текущий код это не влияет);
first() -
отбирает первый элемент списка;
download() - скачивает видео.

Для видео в более хорошем качестве, так же можно отсортировать нужное, но я предпочел в цикле пройтись по элементам это 1080р, 60fps и звук в 128kbps. Опять же мы можем из "streams" взять определенные данные(например разрешения и fps), добавить их в кортеж или список, потом передавать в функцию то, что нам нужно, так же использовать блок try/except и так далее, но здесь используется более простой подход. В моём случае код будет такой:

for i in yt.streams:
if '1080' in str(i) and '60fps' in str(i) and 'mp4' in str(i):
i.download(filename_prefix='video')
if '128kbps' in str(i):
i.download()
filename_prefix нужен, чтобы файл со звуком в формате mp4 не перезаписал файл с видео
filename_prefix нужен, чтобы файл со звуком в формате mp4 не перезаписал файл с видео

Теперь добавляем блок скачивания превью в максимальном качестве, используя метод "yt.thumbnail_url" - это ссылка на превью. В моём случае:

https://i.ytimg.com/vi/<код видео>/hqdefault.jpg?v=5c8941e8

Обратите внимание на "hqdefault". Это говорит о том, в каком качестве мы хотим получить изображение. Этот параметр добавляется в конце, а "?v=5c8941e8" можно вообще опустить. Существует несколько вариантов:

  1. "default.jpg";
  2. "hqdefault.jpg";
  3. "mqdefault.jpg";
  4. "sddefault.jpg";
  5. "maxresdefault.jpg"

Использую не тривиальный способ получения ссылки, разбивая на "https://i.ytimg.com/vi/<код видео>" и качество, можно использовать регулярки, либо индекс слеша с конца строки и тому подобное. Необходимость цикла из-за того, что периодически в ссылке на превью появляется "sd" или "hq". Так я исключаю ошибки и скачиваю видео в лучшем качестве. Добавляю желаемое качество и скачиваю стандартно через "with open". В конце так же добавляется строка с оповещением, что скачивание прошло успешно.

# Блок скачивания превью
lst_quality = ("default.jpg", "hqdefault.jpg", "mqdefault.jpg", "sddefault.jpg",
"maxresdefault.jpg")
for quality in lst_quality:
if quality in str(yt.thumbnail_url):
image = str(yt.thumbnail_url).split(f'/{quality}')
img_data = requests.get(image[0] + '/maxresdefault.jpg').content
with open(f'превью {number_v}.jpg', 'wb') as handler:
handler.write(img_data)

print(f'\nFinished downloading: {yt.title}' + reset_color)
-7

Осталось только запустить объединение видео. Вторая функция:

def concat_video(video_name, audio_name, number):
input_video = ffmpeg.input(video_name)
input_audio = ffmpeg.input(audio_name)
ffmpeg.concat(input_video, input_audio, v=1, a=1).output(f'finished_video{number}.mp4').run()
-8

В функцию передаем имя видео файла с расширением, имя аудио файла с расширением и порядковый номер, если будем рендерить несколько видео(планирую делать именно так). Часть кода по "output('finished_video.mp4')" включает имя файла на выходе после объединения. Атрибуты "v=1, a=1":
v=1: Установите количество выходных видеопотоков, то есть количество видеопотоков в каждом сегменте. По умолчанию 1.
a=1: Установите количество выходных аудиопотоков, то есть количество аудиопотоков в каждом сегменте. По умолчанию 0.

Для детального разбора FFmpeg, рекомендую обратиться к документации, поскольку это не является целью данной статьи.

По итогу, мы научились скачивать видео с ютуб при помощи pytube, отбирать нужное качество, скачивать превью в желаемом качестве, а так же объединять видео и звуковую дорожку для видео при помощи FFmpeg.