Загрузка музыки ВКонтакте

Доброго времени суток всем.


Захотелось мне скачать всю мою музыку со ВКонтакте на флешку, как в старые добрые времена. Немного погуглив и не найдя практически ничего более менее приемлемого, я решил действовать своими силами. Спустя пол часа получился вполне себе рабочий скрипт. Итак, начнём.


Для работы нужно скачать модули vk_api и request!


Для начала подключим необходимые модули и объявим некоторые переменные:


import os
import pickle
import vk_api
import requests

from vk_api import audio

from time import time

vk_file = "vk_config.v2.json"
REQUEST_STATUS_CODE = 200 
path = 'vk_music/'

Теперь напишем метод авторизации в аккаунт ВКонтакте:


def Auth(new=False):
    try:
        USERDATA_FILE = r"AppData/UserData.datab" #файл хранит логин, пароль и id
        global my_id # объявляем переменную глобально, дабы иметь к ней доступ из других методов
                # проверяем, нет ли сохранённых данных авторизации? Если есть, то загружаем
        if (os.path.exists(USERDATA_FILE) and new == False):
            with open(USERDATA_FILE, 'rb') as DataFile:
                LoadedData = pickle.load(DataFile)

            login = LoadedData[0]
            password = LoadedData[1]
            my_id = LoadedData[2]
        else: # если есть, но пользователь выбрал новую авторизацию, то удаляем данных и просим ввести новые
            if (os.path.exists(USERDATA_FILE) and new == True):
                os.remove(USERDATA_FILE)

            login = str(input("Введите логин\n> ")) 
            password = str(input("Введите пароль\n> ")) 
            my_id = str(input("Введите id профиля\n> "))
            SaveUserData(login, password, my_id)

        SaveData = [login, password, my_id]
        with open(USERDATA_FILE, 'wb') as dataFile:
            pickle.dump(SaveData, dataFile) # сохраняем введённые данные

        vk_session = vk_api.VkApi(login=login, password=password)
        try:
            vk_session.auth() # пробуем авторизоваться, если возникнет исключение, значит у пользователя включена двухфакторная аутентификация. Просим ввести код.
        except:
            vk_session = vk_api.VkApi(login=login, password=password, 
                auth_handler=auth_handler) # auth_handler=auth_handler - вызываем метод, см. далее
            vk_session.auth()
        print('Вы успешно авторизовались.')
        vk = vk_session.get_api()
        global vk_audio # объявляем глобально, дабы иметь доступ из других методов
        vk_audio = audio.VkAudio(vk_session)
    except KeyboardInterrupt:
        print('Вы завершили выполнение программы.')

Метод будет проверять, не авторизовывались ли мы уже раньше? Если такое было, то можно будет продолжить в этом аккаунте, или авторизоваться по-новой. В этом случае старые данные будут стёрты.


Далее напишем метод auth_handler, который нужен для авторизации в аккаунтах, в которых включена двухфакторная аутентификация:


def auth_handler():
    code = input("Введите код подтверждения\n> ")
    remember_device = True # True -  запоминаем и не просим каждый раз вводить код
    return code, remember_device

И так, теперь мы можем авторизоваться во ВКонтакте. В методе Auth упомянался метод SaveUserData(), он нужен для сохранения данных. Напишем его:


def SaveUserData(login, password, profile_id):
    USERDATA_FILE = r"AppData/UserData.datab"
    if (not os.path.exists("AppData")): # если нет папки AppData - создадим ее
        os.mkdir("AppData")

    SaveData = [login, password, profile_id] # список данных для сохранения

    with open(USERDATA_FILE, 'wb') as dataFile: # собственно записываем данные в файл
        pickle.dump(SaveData, dataFile)

Данные будут записаны в бинарном виде, дабы не хранить логин и пароль пользователя в открытом виде.


Осталось написать метод загрузки аудио со ВКонтакте, давайте это и сделаем:


def main():
    try:
        if (not os.path.exists("AppData")):
            os.mkdir("AppData")
        if not os.path.exists(path):
            os.makedirs(path)
                # спросим пользователя, нужно ли авторизоваться по-новой или продолжить старую сессию
        auth_dialog = str(input("Авторизоваться заново? yes/no\n> "))
        if (auth_dialog == "yes"):
            Auth(new=True)
        elif (auth_dialog == "no"):
            Auth(new=False)
        else:
            print('Ошибка, неверный ответ.')
            main()
        print('Подготовка к скачиванию...')
        os.chdir(path) #меняем текущую директорию

        audio = vk_audio.get(owner_id=my_id)[0]
        print('Будет скачано:', len(vk_audio.get(owner_id=my_id)), 'аудиозаписей.')
        count = 0
        time_start = time()
        print("Скачивание началось...\n")
                # этим циклом, собственно, и скачиваем нашу музыку.
        for i in vk_audio.get(owner_id=my_id):
            try:
                print('Скачивается: ' + i["artist"] + " - " + i["title"])
                count += 1
                r = requests.get(audio["url"])
                if r.status_code == REQUEST_STATUS_CODE:
                    print('Скачивание завершено: ' + i["artist"] + " - " + i["title"])
                    with open(i["artist"] + ' - ' + i["title"] + '.mp3', 'wb') as output_file:
                        output_file.write(r.content)
            except OSError:
                print("!!! Не удалось скачать аудиозапись №", count)
        time_finish = time()
        print("" + vk_audio.get(owner_id=my_id) + " аудиозаписей скачано за: ", 
                                            time_finish - time_start + " сек.")
    except KeyboardInterrupt:
        print('Вы завершили выполнение программы.')

Ну вот и всё. Теперь у нас есть рабочий скрипт для загрузки аудиозаписей из ВКонтакте.
Вот так выглядит весь исходный код:


Показать исходный код
import os
import pickle
import vk_api
import requests

from vk_api import audio

from time import time

__version__ = 'VK Music Downloader v1.0'

APP_MESSAGE = '''
        _   .   ___
    /\\   |   | | \\  |  |   |  \\    /  | /
   /__\\  |   | |  \\ |  |   |   \\  /   |/
  /    \\ |___| |__/ |  |___|    \\/    |\\
  '''

vk_file = "vk_config.v2.json"
REQUEST_STATUS_CODE = 200 
path = 'vk_music/'

def auth_handler(remember_device=None):
    code = input("Введите код подтверждения\n> ")
    if (remember_device == None):
        remember_device = True
    return code, remember_device

def SaveUserData(login, password, profile_id):
    USERDATA_FILE = r"AppData/UserData.datab"
    SaveData = [login, password, profile_id]

    with open(USERDATA_FILE, 'wb') as dataFile:
        pickle.dump(SaveData, dataFile)

def Auth(new=False):
    try:
        USERDATA_FILE = r"AppData/UserData.datab" #файл хранит логин, пароль и id
        global my_id
        if (os.path.exists(USERDATA_FILE) and new == False):
            with open(USERDATA_FILE, 'rb') as DataFile:
                LoadedData = pickle.load(DataFile)

            login = LoadedData[0]
            password = LoadedData[1]
            my_id = LoadedData[2]
        else:
            if (os.path.exists(USERDATA_FILE) and new == True):
                os.remove(USERDATA_FILE)

            login = str(input("Введите логин\n> ")) 
            password = str(input("Введите пароль\n> ")) 
            my_id = str(input("Введите id профиля\n> "))
            SaveUserData(login, password, my_id)

        SaveData = [login, password, my_id]
        with open(USERDATA_FILE, 'wb') as dataFile:
            pickle.dump(SaveData, dataFile)

        vk_session = vk_api.VkApi(login=login, password=password)
        try:
            vk_session.auth()
        except:
            vk_session = vk_api.VkApi(login=login, password=password, 
                auth_handler=auth_handler)
            vk_session.auth()
        print('Вы успешно авторизовались.')
        vk = vk_session.get_api()
        global vk_audio 
        vk_audio = audio.VkAudio(vk_session)
    except KeyboardInterrupt:
        print('Вы завершили выполнение программы.')

def main():
    try:
        if (not os.path.exists("AppData")):
            os.mkdir("AppData")
        if not os.path.exists(path):
            os.makedirs(path)

        auth_dialog = str(input("Авторизоваться заново? yes/no\n> "))
        if (auth_dialog == "yes"):
            Auth(new=True)
        elif (auth_dialog == "no"):
            Auth(new=False)
        else:
            print('Ошибка, неверный ответ.')
            main()
        print('Подготовка к скачиванию...')
        os.chdir(path) #меняем текущую директорию

        audio = vk_audio.get(owner_id=my_id)[0]
        print('Будет скачано:', len(vk_audio.get(owner_id=my_id)), 'аудиозаписей.')
        count = 0
        time_start = time() # сохраняем время начала скачивания
        print("Скачивание началось...\n")
                # собственно циклом загружаем нашу музыку 
        for i in vk_audio.get(owner_id=my_id):
            try:
                print('Скачивается: ' + i["artist"] + " - " + i["title"]) # выводим информацию о скачиваемой в данный момент аудиозаписи
                count += 1
                r = requests.get(audio["url"])
                if r.status_code == REQUEST_STATUS_CODE:
                    print('Скачивание завершено: ' + i["artist"] + " - " + i["title"])
                    with open(i["artist"] + ' - ' + i["title"] + '.mp3', 'wb') as output_file:
                        output_file.write(r.content)
            except OSError:
                print("!!! Не удалось скачать аудиозапись №", count)
        time_finish = time()
        print("" + vk_audio.get(owner_id=my_id) + " аудиозаписей скачано за: ", 
                                            time_finish - time_start + " сек.")
    except KeyboardInterrupt:
        print('Вы завершили выполнение программы.')

if __name__ == '__main__':
    print(APP_MESSAGE)
    print(__version__ + "\n")
    main()

Я только учусь, поэтому буду рад всем замечаниям в коде. Спасибо за внимание.

Теги:
Python, python 3, vk_api, музыка, вконтакте

Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.