• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Статья Создадим класс для загрузки видео с YouTube, с помощью библиотеки pytube и Python

Для загрузки видео с видеохостинга YouTube существует множество самых разнообразных модулей. Я сам делал небольшую функцию для загрузки с помощью парсинга разнообразных сайтов. Но, лучше, конечно же, использовать модули, которые уже заточены под то, чтобы выполнять определенные функции, а не изобретать велосипед, хотя, тут тоже вопрос спорный, вдруг ваш велосипед окажется лучше )) Я велосипед решил не изобретать, а использовал уже существующую библиотеку pytube. Таким образом, давайте создадим класс для загрузки видео с помощью данной библиотеки и, конечно же, Python.

000.png



Что потребуется?

Создаем файл модуля. Я назвал его просто: downloader.py. После этого нужно установить библиотеки. Конечно же, в данном случае основной библиотекой, которая будет использоваться для загрузки, будет pytube. Для его установки пишем в терминале:

pip install pytube

Также я воспользовался еще двумя библиотеками, которые больше служат для вспомогательных целей, радовать глаз и ухо )). Библиотеку colorama я использовал для того, чтобы раскрасить вывод в терминале, а библиотеку chime для звукового сопровождения окончания загрузки видео, плейлиста или при возникновении ошибки, просто, чтобы привлечь внимание пользователя. Для их установки пишем в терминале:

pip install colorama chime

После установки библиотек выполним импорт всего, что нужно для работы модуля, а также инициализируем модуль colorama:

Python:
import os

from colorama import Fore
from colorama import init
from pytube import exceptions
from pytube import YouTube
from pytube import Playlist
from pytube.cli import on_progress
import chime

init()


Замена символов в названиях видео, приведение размера файла к удобочитаемому формату, загрузка субтитров

Перед тем, как создавать класс, давайте создадим вспомогательные функции, которые будут находиться вне класса. Для начала, создадим функцию get_size(bts, ending='iB'). На вход она принимает размер файла в байтах, а возвращает уже переконвертированное значение. Это нужно для того, чтобы размер видео отображался в более человеко читаемом формате, а именно, килобайтах, мегабайтах и выше.

Создадим переменную size, в которую запишем значение 1024, ведь в одном килобайте 1024 байта, а значит именно на это значение нужно поделить переданное в функцию число, чтобы получить нужный размер. Затем в цикле сравниваем полученное значение с size, и если оно меньше, возвращаем его из функции с суффиксом и двумя цифрами после запятой. Если же нет, делим на size и присваиваем значение bts. Таким образом, как только мы получим нужное значение, оно будет возвращено из функции.

Python:
def get_size(bts, ending='iB'):
    """
    Получает размера файла. Переводит размер в читаемый формат,
    значение возвращает из функции.
    :param bts: размер файла.
    :param ending: суффикс для возвращаемого значения.
    :return: возвращает размер файла в определенном формате.
    """
    size = 1024
    for item in ["", "K", "M", "G", "T", "P"]:
        if bts < size:
            return f"{bts:.2f} {item}{ending}"
        bts /= size

Еще одна функция, которая будет нужна в данном модуле, это функция, которая выполняет замену всяческих знаков в тексте, вроде звездочек, кавычек и прочих. Для чего это нужно? Так как мы будем создавать папку для сохранения видео с названием канала или плейлиста, значит, нужно привести название к такому виду, чтобы его приняла операционная система. То же самое относиться и к названию видео.

Создадим функцию rep_symbol(text: str). На вход она получает текст, в котором нужно заменить символы, а возвращает уже очищенное значение. Здесь ничего мудрить не надо. Я просто сделал длинную строку, в которой перечислил все, что нужно заменять. Можно было бы использовать цикл, но почему-то я столкнулся с тем, что не все символы в нем заменяются. Это требует дополнительных экспериментов или нужно просто порыться в интернете. Тем не менее, работа данного выражения меня, пока, устраивает. Вот что у меня получилось:

Python:
def rep_symbol(text: str):
    """
    Заменяются символы, которые могут вызвать ошибку при сохранении
    видео или создании папки для него.
    :param text: текст для замены символов.
    :return: возвращает результат после замены символов.
    """
    tex = text.replace("'", "").replace('"', '').replace('|', '_').replace(' | ', '_').replace('/', '_'). \
        replace('\\', '_').replace('*', '_').replace('?', '').replace('<', '').replace('>', '_').replace(':', ''). \
        replace(';', '').replace('.', '').strip()
    return tex

Еще одна функция вне класса, из которой будем запускать все основные действия. Создадим функцию download(url: str, res: str). На вход она получает два значения — первое значение, это ссылка на видео или канал YouTube, второе значение — разрешение, которое нужно для скачивания. Для начала создаем экземпляр нашего будущего класса, в который передаем полученные значения. И после запускаем функцию start, созданную уже в классе.

Python:
def download(url: str, res: str):
    """
    Создает экземпляр класса YtDownload, в который
    передаются полученные параметры. Вызывается функция
    проверки ссылок и определения типа загружаемого видео.
    :param url: ссылка на видео или на плейлист.
    :param res: желаемое качество видео.
    """
    tube = YtDownload(url=url, res=res)
    tube.start()

Функция, которая потребуется для работы класса, но создания ее в классе не требуется. Это функция для загрузки субтитров. Почти у каждого видео есть субтитры, которые создаются автоматически. И иногда, попадаются субтитры загруженные пользователем. Мы будем загружать английские субтитры, если вы будете скачивать видео на английском языке. Они помогут понять то, о чем говорится в видео, ну или их можно просто перевести.

Создадим функцию srt_download(cap, path_save: str, title: str). На вход она принимает субтитры, путь для их сохранения и заголовок видео, для которого субтитры скачиваются. Запускаем цикл по ключам в заголовках, ищем в них автоматически созданные «a.en» или загруженные пользователем «en» субтитры. Если находим, сохраняем в файл с помощью функции pytube generate_srt_captions().

Но, здесь хочу сразу же сказать, что в том pytube, который устанавливается с помощью pip данная функция не работает. Это давняя проблема, которую почему-то разработчики решать не торопятся. Однако, покопавшись в форумах я нашел решение. Проблема заключается в неверном парсинге xml, код которого находиться в модуле captions.py. Я нашел целый код модуля, создал его и заменяю, если мне нужно загрузить, как в данном случае, субтитры. Модуль будет во вложении. Путь для замены в виртуальном окружении (примерный): venv/lib/python3.8/site-packages/pytube. А также, путь для замены в питоне вне виртуального окружения: .local/lib/python3.8/site-packages (12.1.0). С версией вашего питона определитесь сами )).

Python:
    for item in cap.lang_code_index.keys():
        if item == 'a.en' or item == 'en':
            with open(os.path.join(path_save, f'{rep_symbol(title)}.srt'), 'w', encoding='utf-8') as file:
                file.write(cap[item].generate_srt_captions())

Python:
def srt_download(cap, path_save: str, title: str):
    """
    Получает список доступных субтитров, проверяет, есть ли
    субтитры с требуемым языком. Если есть, загружает.
    Для данной функции требуется замена файла captions.py
    в папке библиотеки pytube. Если не заменить, вызывается
    ошибка парсинга. Проблема давняя, но почему-то официально
    не решенная. Однако, на форумах нашел правильный вариант.
    :param cap: список субтитров из видео.
    :param path_save: путь для загрузки субтитров.
    :param title: название для загружаемых субтитров.
    """
    for item in cap.lang_code_index.keys():
        if item == 'a.en' or item == 'en':
            with open(os.path.join(path_save, f'{rep_symbol(title)}.srt'), 'w', encoding='utf-8') as file:
                file.write(cap[item].generate_srt_captions())

Созданные функции можно расположить как перед, так и после класса. Но, наверное, будет красивее и правильнее расположить их, все же после.

Что ж, на этом подготовительные работы можно завершить. Теперь пришла очередь писать логику класса.


Создание класса. Инициализация

Создадим класс YtDownload и функцию инициализации параметров __init__(self, url, res). Как видим, на входе данный класс принимает ссылку на видео или плейлист, а также желаемое качество для загружаемого видео. Их и нужно инициализировать. Инициализируем ссылку. А вот для разрешения пришлось написать условие, так как, почему-то не срабатывал параметр по умолчанию. То есть, если я передавал пустую строку, то она и подставлялась в значение res, вместо того, что было определено. Поэтому, делаем проверку, и, если пользователь не ввел значения, присваиваем разрешению видео значение 720.

Python:
class YtDownload:
    def __init__(self, url, res):
        """
        Инициализируются параметры, переданные в класс.
        Выполняется проверка на переданное значение разрешения.
        Значение по умолчанию не подставляется, если передана
        пустая строка, подставляется именно пустая строка.
        :param url: ссылка на видео или плейлист YouTube.
        :param res: желаемое разрешение видео.
        """
        self.url = url
        if res == '':
            self.res = '720p'
        else:
            self.res = f'{res}p'


Стартовая функция

Создадим функцию start(self), с которой и будет начинаться работа по загрузке видео. В данной функции проверим, является ли переданная ссылка ссылкой на YouTube. Для этого напишем условие, в котором проверяем, есть ли в переданной ссылке «youtube.com». Если нет, тогда сообщаем пользователю, что его ссылка не валидна и подаем звуковой сигнал.

Python:
        if 'youtube.com' not in self.url:
            print(Fore.RED + 'Неверная ссылка')
            chime.theme('mario')
            chime.success()
            return

Делаем проверку для определения, является ли переданная ссылка ссылкой на плейлист или просто на видео. Для этого ищем в ссылке слово «playlist». Если находим, запускаем функцию загрузки видео из плейлиста. Если не находим, создаем экземпляр класса YouTube, передаем в него полученные параметры, определяем путь к загружаемому видео и запускаем функцию загрузки видео, куда передаем созданный экземпляр класса и путь для загружаемого видео. Также заключим создание экземпляра в блок try — excepr, для того, чтобы обработать исключение в случае, если переданная пользователем ссылка будет с ошибками.

Python:
        if 'playlist' in self.url:
            self.playlist_download()
        else:
            try:
                yt = YouTube(self.url, on_progress_callback=on_progress)
                path_save = os.path.join(os.getcwd(), 'video', f'{rep_symbol(yt.author)}')
                self.video_download(yt, path_save)
            except exceptions.RegexMatchError:
                print(Fore.RED + 'Неверная ссылка')
                chime.theme('mario')
                chime.success()
                return

Python:
    def start(self):
        """
        Выполняется проверка, является ли ссылка валидной для YouTube.
        Определяется тип переданной ссылки (плейлист или видео).
        В зависимости назначения ссылки запускается загрузка или
        плейлиста, или создается экземпляр класса YouTube с параметрами,
        устанавливается путь для загрузки видео, запускается функция
        загрузки видео с переданными параметрами.
        :return: выход из функции.
        """
        if 'youtube.com' not in self.url:
            print(Fore.RED + 'Неверная ссылка')
            chime.theme('mario')
            chime.success()
            return

        if 'playlist' in self.url:
            self.playlist_download()
        else:
            try:
                yt = YouTube(self.url, on_progress_callback=on_progress)
                path_save = os.path.join(os.getcwd(), 'video', f'{rep_symbol(yt.author)}')
                self.video_download(yt, path_save)
            except exceptions.RegexMatchError:
                print(Fore.RED + 'Неверная ссылка')
                chime.theme('mario')
                chime.success()
                return


Проверка разрешения видео

Создадим функцию, которая будет проверять, является ли разрешение видео, которое передано пользователем доступно для загрузки. Я назвал функцию check_res(self, yt). На вход она принимает экземпляр созданного класса YouTube с параметрами.

Объявим пустой список, в который будем складывать полученные разрешения. С помощью функции streams.filter получим доступные для загрузки.

Python:
        res_list = []
        check_res = yt.streams.filter(progressive=True)

Теперь в цикле заберем все разрешения из полученного списка. Для этого придется заменить в каждом элементе некоторые значения, предварительно приведя его к строке и разбив по пробелам. После того, как мы сформируем список, делаем проверку, входит ли в него полученное от пользователя разрешение. Если да, возвращаем его обратно, если нет, возвращаем последнее значение из списка, так как именно оно является лучшим качеством для загрузки в данном контексте.

Python:
        for item in check_res:
            res_list.append(str(item).split()[3].replace("res=", '').replace('"', ''))
        if self.res not in res_list:
            print(Fore.YELLOW + f'\nЗапрошенное разрешение недоступно\nБудет загружено: {res_list[-1]}\n')
            return res_list[-1]

        return self.res

Python:
    def check_res(self, yt):
        """
        Проверка разрешения введенного пользователем. Если требуемое разрешение недоступно,
        либо отсутствует разрешение для видео по умолчанию, возвращается максимально доступное
        разрешение доступное для данного видео.
        :param yt: экземпляр класса YouTube с параметрами.
        :return: возвращает разрешение видео.
        """
        res_list = []
        check_res = yt.streams.filter(progressive=True)

        for item in check_res:
            res_list.append(str(item).split()[3].replace("res=", '').replace('"', ''))
        if self.res not in res_list:
            print(Fore.YELLOW + f'\nЗапрошенное разрешение недоступно\nБудет загружено: {res_list[-1]}\n')
            return res_list[-1]

        return self.res


Загрузка одного видео

Для загрузки одного видео создадим функцию video_download(self, yt, path_save: str). На вход она принимает экземпляр класса YouTube с параметрами и путь для сохранения видео. Именно с помощью данной функции мы будем обрабатывать все видео входящие в плейлист.

Проверим разрешение загружаемого видео. Выведем сообщения в терминал о названии видео, авторе, размере и загружаемом разрешении.

Python:
        res = self.check_res(yt)

        print(Fore.GREEN + f'\nЗагружаю видео\n{"-" * 14}\n')
        print(Fore.YELLOW + f'  Название: "{yt.title}"')
        print(Fore.YELLOW + f'  Автор: "{yt.author}"')
        print(Fore.YELLOW + f'  Размер видео: '
                            f'{get_size(yt.streams.filter(progressive=True, resolution=res).first().filesize)}')
        print(Fore.YELLOW + f'  Качество: {res}\n')
        print(Fore.GREEN + f"{'-' * 14}\n")

Теперь создадим поток для загрузки видео, в котором отфильтруем только те видео, параметр progressive которых равен True, а также передадим в фильтр требуемое разрешение. Дополнительных проверок здесь уже не требуется, так как мы проверили разрешение ранее. Передадим путь для загрузки видео и название, которое заберем из его названия на сервисе. Выведем в терминал прогресс-бар.

После того, как видео будет загружено, попробуем загрузить субтитры. Так как если видео на английском языке, они могут понадобиться для лучшего понимания. Или их можно просто перевести с помощью переводчика. Запускаем функцию загрузки субтитров, в которую передаем параметр caption. Именно в нем находятся субтитры. Путь для сохранения субтитров, который будет таким же, как путь загружаемого видео и название видео взятое с сервиса с удаленными символами, которые будут препятствовать сохранению. После чего выводим сообщение о том, что видео загружено и проигрываем звуковое оповещение.

Python:
        yt.streams.filter(progressive=True, resolution=res).first().download(path_save, f'{rep_symbol(yt.title)}.mp4')
        yt.register_on_progress_callback(on_progress)
        srt_download(yt.captions, path_save, rep_symbol(yt.title))

        print(Fore.BLUE + f'Видео загружено в папку: {os.path.join(path_save, f"{rep_symbol(yt.title)}.mp4")}\n')
        chime.theme('big-sur')
        chime.success()

Python:
    def video_download(self, yt, path_save: str):
        """
        Загружает видео, которое передается в экземпляре класса YouTube с параметрами.
        Также передается путь для загрузки видео, который различается, в случае, если
        загружается плейлист.
        Выводиться в терминал информация о видео.
        :param yt: экземпляр класса YouTube с параметрами.
        :param path_save: путь к папке для загрузки видео.
        """
        res = self.check_res(yt)

        print(Fore.GREEN + f'\nЗагружаю видео\n{"-" * 14}\n')
        print(Fore.YELLOW + f'  Название: "{yt.title}"')
        print(Fore.YELLOW + f'  Автор: "{yt.author}"')
        print(Fore.YELLOW + f'  Размер видео: '
                            f'{get_size(yt.streams.filter(progressive=True, resolution=res).first().filesize)}')
        print(Fore.YELLOW + f'  Качество: {res}\n')
        print(Fore.GREEN + f"{'-' * 14}\n")

        yt.streams.filter(progressive=True, resolution=res).first().download(path_save, f'{rep_symbol(yt.title)}.mp4')
        yt.register_on_progress_callback(on_progress)
        srt_download(yt.captions, path_save, rep_symbol(yt.title))

        print(Fore.BLUE + f'Видео загружено в папку: {os.path.join(path_save, f"{rep_symbol(yt.title)}.mp4")}\n')
        chime.theme('big-sur')
        chime.success()


Загрузка плейлиста

Если пользователь передаст ссылку не на видео, а на плейлист, у нас должна быть функция, которая умеет загружать все видео из него. Создадим функцию playlist_download(self). Для начала создаем экземпляр класса Playlist, в который передаем ссылку. Затем посчитаем количество символов в сообщении, которое будем передавать при выводе в терминал «-», для отделения строк. Но это так, больше для декорации. Ну и, собственно, выведем само сообщение о загрузке плейлиста.

Python:
            pl = Playlist(self.url)
            width_name = len(Fore.GREEN + f'Загружаю плейлист: "{pl.title}"')
            print(Fore.GREEN + f'\n{"-" * (width_name - 5)}\nЗагружаю плейлист: "{pl.title}"\n{"-" * (width_name — 5)}')

Запустим цикл, в котором пробежимся по списку полученных видео плейлиста. Для каждого видео будем создавать экземпляр класса YouTube, в который передадим нужные параметры. А именно ссылку на видео и определим параметр, который отвечает за функцию, используемую для показа прогресс-бара. Сформируем путь для загрузки видео. В данном случае, так как это плейлист, он будет отличаться от пути, по которому загружается одиночное видео. Передадим эти параметры в функцию загрузки видео. Так будем продолжать, пока ссылки в плейлисте не закончатся.

Python:
            for url in pl.video_urls:
                yt = YouTube(url, on_progress_callback=on_progress)
                path_save = os.path.join('video', rep_symbol(yt.author), rep_symbol(pl.title))
                self.video_download(yt, path_save)

После того, как все видео из плейлиста будут загружены, выведем сообщение об этом в терминал и просигнализируем звуковым оповещением.

Python:
            print(Fore.GREEN + f'{"-" * (width_name - 5)}\nПлейлист: "{pl.title}" загружен\n{"-" * (width_name - 5)}\n')
            chime.theme('big-sur')
            chime.success()

Обернем все в блок try — except, для того, чтобы обработать исключение, если пользователь передаст неправильную ссылку.

Python:
    def playlist_download(self):
        """
        Загрузка плейлиста с видео. Для каждого видео из списка полученного из плейлиста
        создается экземпляр класса YouTube с параметрами. Он передается в функцию загрузки
        видео, где выполняется его загрузка.
        :return: выход из функции.
        """
        try:
            pl = Playlist(self.url)
            width_name = len(Fore.GREEN + f'Загружаю плейлист: "{pl.title}"')
            print(Fore.GREEN + f'\n{"-" * (width_name - 5)}\nЗагружаю плейлист: "{pl.title}"\n{"-" * (width_name - 5)}')
            for url in pl.video_urls:
                yt = YouTube(url, on_progress_callback=on_progress)
                path_save = os.path.join('video', rep_symbol(yt.author), rep_symbol(pl.title))
                self.video_download(yt, path_save)
            print(Fore.GREEN + f'{"-" * (width_name - 5)}\nПлейлист: "{pl.title}" загружен\n{"-" * (width_name - 5)}\n')
            chime.theme('big-sur')
            chime.success()
        except KeyError:
            print(Fore.RED + 'Неверная ссылка')
            chime.theme('mario')
            chime.success()
            return


Пример работы с классом

Для примера создадим модуль main.py. Импортируем модуль с созданным нами классом.

from downloader import download

И вызовем функцию загрузки видео, в которую передадим запрошенные у пользователя ссылку и разрешение.

Python:
download(url=input('Введите ссылку на видео или плейлист: '),
         res=input('Введите желаемое качество видео (360, 480, 720): '))

Также, данную функцию можно использовать для загрузки в цикле видео из списка. Для примера:

Python:
with open('link.txt', 'r', encoding='utf-8') as file:
    reader = file.readlines()
    for link in reader:
        download(link, '720')


Небольшое видео демонстрации работы класса:


А на этом, пожалуй, все.

Спасибо за внимание. Надеюсь, что данная информация будет вам полезна
 

Вложения

  • downloader.zip
    5 КБ · Просмотры: 189

chebotarelli

One Level
06.01.2020
4
1
BIT
0
Супер, спасибо за труды =)

Пытался искать, но не нашел, возможно есть решение по загрузке треков с Яндекс.Музыка? Подписки есть, но не всегда на трассе устойчивый инет)))
 

Johan Van

Green Team
13.06.2020
355
671
BIT
241
Супер, спасибо за труды =)

Пытался искать, но не нашел, возможно есть решение по загрузке треков с Яндекс.Музыка? Подписки есть, но не всегда на трассе устойчивый инет)))

Пока не знаю. Но поискать попробую.
UPD: Если просто зайти на Яндекс Музыку, и навести мышку на трек, то там будет кнопка скачать в виде стрелки вниз. По поводу подписки не скажу. У меня ее нет. Почитал, что музыку по подписке скачать тоже возможно. Единственное, что прослушивать ее можно будет только через плеер от Яндекса. Потому, что она в зашифрованном виде и будет воспроизводиться ровно до того времени по какое у вас есть подписка. Вроде бы так.

Снимок экрана от 2022-08-25 05-39-59.png


Это если вручную. Ну и есть много браузерных расширений для этого . Я поковыряюсь в их странице, может что-то получиться найти. Но с Яндексом всегда сложно было в этом плане. Да еще он "психованный", любое обращение не через браузер воспринимает как автоматический запрос, то есть от робота и просит ввести капчу. Пробовал даже через selenium стелс, но даже он не помог ))
 
Последнее редактирование:
  • Нравится
Реакции: chebotarelli

chebotarelli

One Level
06.01.2020
4
1
BIT
0
Единственное, что прослушивать ее можно будет только через плеер от Яндекса. Потому, что она в зашифрованном виде и будет воспроизводиться ровно до того времени по какое у вас есть подписка. Вроде бы так.
Все так, но в моем случае было бы здорово все залить на внутренний хард авто и с него в любой момент времени слушать "фонотеку" =)
Да и с учетом нынешней ситуации можно проснуться по утру и обнаружить что половина треков пропала из за того что зарубежный правообладатель откатил права.

Это если вручную. Ну и есть много браузерных расширений для этого дела. Я поковыряюсь в их странице, может что-то получиться найти. Но с Яндексом всегда сложно было в этом плане. Да еще он "психованный", любое обращение не через браузер воспринимает как автоматический запрос, то есть от робота и просит ввести капчу. Пробовал даже через selenium стелс, но даже он не помог ))
Спасибо) Вчера я все таки озадачился этим вопросом и нашел вот такое расширение - GitHub - sodeprecated/yandex-music-downloader: Google chrome browser extension for downloading tracks from Yandex Music . Все отработало на ура и одна коллекция из 350 треков была выкачана разом.
 

Johan Van

Green Team
13.06.2020
355
671
BIT
241
Все так, но в моем случае было бы здорово все залить на внутренний хард авто и с него в любой момент времени слушать "фонотеку" =)
Да и с учетом нынешней ситуации можно проснуться по утру и обнаружить что половина треков пропала из за того что зарубежный правообладатель откатил права.


Спасибо) Вчера я все таки озадачился этим вопросом и нашел вот такое расширение - GitHub - sodeprecated/yandex-music-downloader: Google chrome browser extension for downloading tracks from Yandex Music . Все отработало на ура и одна коллекция из 350 треков была выкачана разом.

Браузерным расширениям проще, потому, что они уже работают в браузере и тем более, на авторизованной странице. В случае же с питоном, все равно, для загрузки музыки нужна будет авторизация. Если не авторизоваться, ссылки на загрузку трека просто не найти. Она не подгружается. А если и подгружается, то есть можно попробовать выловить ее в плеере, то там всего 30 секунд из трека. Что не есть хорошо. ))

Еще я тут чуть поковырялся. У Яндекса какая-то странная страница авторизации. То есть, авторизоваться то не проблема. Но не пойму пока что, от чего зависит, просит он подтвердить, что это действительно ваш аккаунт или не просит. Сначала у меня просил, потом перестал. Потом вместо последовательной авторизации стал выкидывать два поля на одной странице. В общем, тут нужно еще подумать ))
 
  • Нравится
Реакции: chebotarelli

InternetMC

Green Team
13.01.2019
11
4
BIT
184
Не будет работать скрипт, если на видео установлено возрастное ограничение.
 
  • Нравится
Реакции: Johan Van

Johan Van

Green Team
13.06.2020
355
671
BIT
241
Не будет работать скрипт, если на видео установлено возрастное ограничение.
Спасибо за тестирование функции. Не думал о возрастных ограничениях. В таком случае, думаю, нужно добавить обработку исключения в функцию check_res(self, yt):

Python:
    def check_res(self, yt):
        """
        Проверка разрешения введенного пользователем. Если требуемое разрешение недоступно,
        либо отсутствует разрешение для видео по умолчанию, возвращается максимально доступное
        разрешение доступное для данного видео.
        :param yt: экземпляр класса YouTube с параметрами.
        :return: возвращает разрешение видео.
        """
        try:
            res_list = []
            check_res = yt.streams.filter(progressive=True)

            for item in check_res:
                res_list.append(str(item).split()[3].replace("res=", '').replace('"', ''))
            if self.res not in res_list:
                print(Fore.YELLOW + f'\nЗапрошенное разрешение недоступно\nБудет загружено: {res_list[-1]}\n')
                return res_list[-1]
            return self.res
        except AgeRestrictedError:
            return False

Ну и соответственно, проверку возвращаемого из данной функции значения там, где она используется.

Python:
    def video_download(self, yt, path_save: str):
        """
        Загружает видео, которое передается в экземпляре класса YouTube с параметрами.
        Также передается путь для загрузки видео, который различается, в случае, если
        загружается плейлист.
        Выводиться в терминал информация о видео.
        :param yt: экземпляр класса YouTube с параметрами.
        :param path_save: путь к папке для загрузки видео.
        """
        if res := self.check_res(yt):

            print(Fore.GREEN + f'\nЗагружаю видео\n{"-" * 14}\n')
            print(Fore.YELLOW + f'  Название: "{yt.title}"')
            print(Fore.YELLOW + f'  Автор: "{yt.author}"')
            print(Fore.YELLOW + f'  Размер видео: '
                                f'{get_size(yt.streams.filter(progressive=True, resolution=res).first().filesize)}')
            print(Fore.YELLOW + f'  Качество: {res}\n')
            print(Fore.GREEN + f"{'-' * 14}\n")

            yt.streams.filter(progressive=True, resolution=res).first().download(path_save, f'{rep_symbol(yt.title)}.mp4')
            yt.register_on_progress_callback(on_progress)
            srt_download(yt.captions, path_save, rep_symbol(yt.title))

            print(Fore.BLUE + f'Видео загружено в папку: {os.path.join(path_save, f"{rep_symbol(yt.title)}.mp4")}\n')
            chime.theme('big-sur')
            chime.success()
            return True
        else:
            return False

Python:
    def playlist_download(self):
        """
        Загрузка плейлиста с видео. Для каждого видео из списка полученного из плейлиста
        создается экземпляр класса YouTube с параметрами. Он передается в функцию загрузки
        видео, где выполняется его загрузка.
        :return: выход из функции.
        """
        try:
            pl = Playlist(self.url)
            width_name = len(Fore.GREEN + f'Загружаю плейлист: "{pl.title}"')
            print(Fore.GREEN + f'\n{"-" * (width_name - 5)}\nЗагружаю плейлист: "{pl.title}"\n{"-" * (width_name - 5)}')
            for url in pl.video_urls:
                yt = YouTube(url, on_progress_callback=on_progress)
                path_save = os.path.join('video', rep_symbol(yt.author), rep_symbol(pl.title))
                if not self.video_download(yt, path_save):
                    print("Видео не может быть загружено. Возрастные ограничения.")
                    chime.theme('mario')
                    chime.success()
                    continue
            print(Fore.GREEN + f'{"-" * (width_name - 5)}\nПлейлист: "{pl.title}" загружен\n{"-" * (width_name - 5)}\n')
            chime.theme('big-sur')
            chime.success()
        except KeyError:
            print(Fore.RED + 'Неверная ссылка')
            chime.theme('mario')
            chime.success()
            return

Python:
    def start(self):
        """
        Выполняется проверка, является ли ссылка валидной для YouTube.
        Определяется тип переданной ссылки (плейлист или видео).
        В зависимости назначения ссылки запускается загрузка или
        плейлиста, или создается экземпляр класса YouTube с параметрами,
        устанавливается путь для загрузки видео, запускается функция
        загрузки видео с переданными параметрами.
        :return: выход из функции.
        """
        if 'youtube.com' not in self.url:
            print(Fore.RED + 'Неверная ссылка')
            chime.theme('mario')
            chime.success()
            return

        if 'playlist' in self.url:
            self.playlist_download()
        else:
            try:
                yt = YouTube(self.url, on_progress_callback=on_progress)
                path_save = os.path.join(os.getcwd(), 'video', f'{rep_symbol(yt.author)}')
                if not self.video_download(yt, path_save):
                    print("Видео не может быть загружено. Возрастные ограничения.")
                    chime.theme('mario')
                    chime.success()
                    return
            except exceptions.RegexMatchError:
                print(Fore.RED + 'Неверная ссылка')
                chime.theme('mario')
                chime.success()
                return

Измененный модуль downloader прикрепил к ответу. К сожалению, я не знаю способа скачать данное видео. Возможно, в этом может помочь
, но его я не тестировал.
 

Вложения

  • downloader.zip
    3,2 КБ · Просмотры: 28
  • Нравится
Реакции: InternetMC

InternetMC

Green Team
13.01.2019
11
4
BIT
184
Да, pytube не имеет встроенной поддержки входа. Придется смотреть в сторону youtube-dl
 

Johan Van

Green Team
13.06.2020
355
671
BIT
241
Да, pytube не имеет встроенной поддержки входа. Придется смотреть в сторону youtube-dl
На то они и возрастные ограничения )). Без авторизации скачать не получиться. Можно конечно попытаться реализовать с помощью selenium. Но это будет уже не так удобно. А youtube-dl вполне может поддерживать. При этом его можно использовать из питона. Впрочем, думаю, что гадать бессмысленно. Лучше читать документацию ))
 

satfan

Green Team
26.06.2022
77
1
BIT
56
Скрипт просто супер !
1080p -- почему не могу выбрать и загрузить это качество.
 

Johan Van

Green Team
13.06.2020
355
671
BIT
241
Скрипт просто супер !
1080p -- почему не могу выбрать и загрузить это качество.
Потому, что если мне не изменяет память, pytube скачивает только видео с параметром progressive="True", то есть, данное видео в формате mp4.
Насколько я знаю, даже при скачивании через сайт, если мне нужно разрешение больше 720p, мне приходиться ждать, пока выполниться конвертация.

Посмотрите на теги, которые возвращает pytube с параметром progressive="True":

Python:
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="24fps" vcodec="avc1.42001E" acodec="mp4a.40.2" progressive="True" type="video">
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="24fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">

Разрешения 1080p здесь нет. Вот потому у вас и не получается скачать видео больше, чем то, что указано как максимальное, т.е. 720p.
 

Johan Van

Green Team
13.06.2020
355
671
BIT
241
Скрипт просто супер !
1080p -- почему не могу выбрать и загрузить это качество.
Чтобы было понятнее, вот все тэги, которые загружает pytube:

screenshot2.png


Можете самостоятельно поэкспериментировать с кодом и добавить недостающую функцию.
 

Johan Van

Green Team
13.06.2020
355
671
BIT
241
Разрешения 1080p здесь -- есть Скачал = 98 мб
Да, оно есть. Но скриптом не поддерживается. Можете добавить поддержку в скрипт. Если, конечно, pytube качает файлы отличные от mp4. Не проверял. В данный момент нет времени на проверку работоспособности с разрешением выше 1080. Будет время - проверю. Пока что, однозначно сказать не могу. Но... если у вас есть время, можете поэкспериментировать и рассказать о результатах.
 

satfan

Green Team
26.06.2022
77
1
BIT
56

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

 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!