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

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


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


Для работы нужно скачать модули 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()

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

Похожие публикации

Средняя зарплата в IT

113 000 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 5 537 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 45

    –2
    Есть savefromnet для chorma'а. Открываете его в своей музыке и нажимаете скачать все. Ждете. Profit.
    А потом еще можно пронумеровать имя файла что б слушать в том же порядке. Для Linux когда давно писал скрипт.
    Заголовок спойлера
    a = 1
    ls -1t | while read line
    do
    let a=a+1
    echo "***"
    b=${a}${line}
    
    mv "$line" "$b"
    echo "***"
    done
    

      +3
      Ну… готовая тулза не решает всего спектра вопросов, который можно решить скриптом. Например автоматическая синхронизация медиа вконтакте на диск дома или в свое, родное облачко.
        0
        Вы конечно правы. На то и готовая тулза, что бы решать только типовые задачи.
        +3
        Не этот ли плагин попалился недавно на воровстве данных юзеров?
          0
          Если своё vk_api подпихнуть, то вполне можно так сделать…
          +8
          Скажу честно — всегда безопаснее написать свой скрипт и быть уверенным, что твои данные останутся при тебе. А savefromnet не опенсорсный, и понять что у него под капотом происходит — нужно немного запариться.
            0
            А вас не смущает savefromnet-у отдавать свои креды от аккаунта? Вы же не знаете что он может еще делать помимо скачивания. Лично я для этого плагина отдельный аккаунт завел и отдельный браузер
            • НЛО прилетело и опубликовало эту надпись здесь
              0

              А зачем на каждый чих вызывать vk_audio.get()?
              Можно же вызвать один раз и записать в переменную. Или я чего то не понимаю.

                0
                Можно, но в статье написано что человек учится. Все с опытом приходит.
                +6
                Youtube-dl справляется с этой задачей и еще с миллионами подобных. Опен-сорс, данные не крадет, скрипты в браузер ставить не нужно.
                  0
                  Справляется ли? Последняя версия получает отлуп с «bad browser», хотя и представляется Хромом.
                  • НЛО прилетело и опубликовало эту надпись здесь
                      0
                      По ссылке официальный сайт, а не исполняемый файл. Где вы увидели youtube-dl.exe?
                      • НЛО прилетело и опубликовало эту надпись здесь
                          +1
                          На этой же странице ссылка на исходный код и документацию. Почему вас так расстраивает наличие возможности скачать исполняемый файл (один из вариантов)?
                          • НЛО прилетело и опубликовало эту надпись здесь
                              +1

                              Кто-то мешает вам собрать его самостоятельно? Сам факт наличия уже собранной версии по-вашему как-то очерняет программу?


                              P.S. gentoo?

                              • НЛО прилетело и опубликовало эту надпись здесь
                                  +1

                                  Повторю вопрос — Gentoo? :)

                                  • НЛО прилетело и опубликовало эту надпись здесь
                                      0

                                      Обычно паранойа такого уровня это признак гентушника. Если вы до сих пор не знакомы с этой операционной системой, то с вашими взглядами на open source у вас просто нет выбора, — вы созданы друг для друга.

                                      • НЛО прилетело и опубликовало эту надпись здесь
                                          0
                                          Крайне мала.
                                            +1

                                            Более, чем реальна. Но уровень паранойи — это выбор каждого. Я вот скорее скачаю готовый deb пакет из источника к которому питаю доверие, нежели буду качать исходные коды, изучать их под лупой и пересобирать. Свои риски я осознаю, и сам же за них несу ответственность. 99%+ пользователей будет качать уже собранный бинарник, и не по глупости, а целенаправлено. Желающим же большего контроля доступны исходные коды. Вы из вторых — велкам, для вас там всё подготовлено.


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


                                            Хехе, представил себе как каждая вторая рабочая станция качает обновления в виде сырсов и ночами пересобирает сама себя. Дивный мир.

                                              0
                                              Хехе, представил себе как каждая вторая рабочая станция качает обновления в виде сырсов и ночами пересобирает сама себя. Дивный мир.


                                              Для таких можно компилятор правильный подсунуть, который будет вкомпиливать правильные дополнения )
                                              • НЛО прилетело и опубликовало эту надпись здесь
                                        0
                                        А какое решение вы предлагаете? Всех заставлять ставить из исходников? Ну так мало просто скачать и скомпилировать, надо же еще тогда глазами проверить на то, что код чист. То есть всем надо внезапно стать программистами, способными пропускать через себя десятки и сотни тысяч строк кода, чтобы не стать жертвой какой-нибудь схемы.
                                        • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          А можно код хотя бы на gist.github.com выложить?
                            –2
                            Я давненько писал бота для скачивания музыки, для себя, vk.com/music_bott некоторые функции уже не работают, но музыку скачать все ещё можно, удобно что не нужно уходить с сайта чтобы скачать музыку, может кому пригодится.
                              +1

                              Учебный пример — это хорошо. Но в свете последних новшевств музыка ВК действительно того стоит, чтобы ей пользоваться?

                                0
                                О каких новшествах идет речь?
                                  +1
                                  Да, там можно найти (и утянуть себе на плеер) музыку, которую в других местах найти трудно.
                                    +1
                                    В свете последних новшеств как раз и стоит. Плейлисты и большое количество вполне себе легальной музыки (работает с крупными агрегаторами, в результате чего там действительно много контента), а также куча авторских сообществ и тематических пабликов с остальной музыкой (и тоже с альбомами в виде плейлистов) формируют просто отличную библиотеку. Плюс пользовательский контент и возможность загрузки своих файлов. При этом цена на подписку вполне гуманна.

                                    Есть и минусы, конечно — некоторая хаотичность и полная чушь в рекомендациях, да.
                                      +1
                                      некоторая хаотичность и полная чушь в рекомендациях


                                      мне, яндекс.музЫка, недавно (2 дня назад как) порекомендовал сборку в стиле «регги», среди прочего там — песня — «течёт река волга» (не знаю кто поёт, какое-то ВИА или ещё кто), игра на балалайке, парочку песен из бродвейских мюзиклов и прочая муть со всех концов планеты… всё что их объединяет с «регги», так это года — почти все композиции родом из 60х +\-
                                    +2
                                    Оффтоп:
                                    Данные будут записаны в бинарном виде, дабы не хранить логин и пароль пользователя в открытом виде.
                                    Так всё равно данные можно прочитать hex-редактором, так как они не зашифорованы. Не по теме статьи, но всё же.

                                    А за скрипт респект, просто и практично.
                                      +1
                                      Они блокнотом читаются, проверено ))
                                      0
                                      Вы успешно авторизовались.
                                      Подготовка к скачиванию...
                                      Traceback (most recent call last):
                                        File "vkdl.py", line 119, in <module>
                                          main()
                                        File "vkdl.py", line 93, in main
                                          audio = vk_audio.get(owner_id=my_id)[0]
                                        File "/home/onboard/.local/lib/python3.6/site-packages/vk_api/audio.py", line 96, in get
                                          return list(self.get_iter(owner_id, album_id, access_hash))
                                        File "/home/onboard/.local/lib/python3.6/site-packages/vk_api/audio.py", line 77, in get_iter
                                          filter_root_el={'class_': 'audioPlaylist__list'} if album_id else None
                                        File "/home/onboard/.local/lib/python3.6/site-packages/vk_api/audio.py", line 236, in scrap_data
                                          for audio in root_el.find_all('div', {'class': 'audio_item'}):
                                      AttributeError: 'NoneType' object has no attribute 'find_all'
                                      


                                      И, действительно, ноды с id == au_search_items в коде страницы с аудио нет.
                                        +1
                                        Теперь на некоторых пользователях тестируется отдача музыки через HLS (M2TS поток), который поддерживает шифрование, так что придётся использовать инструменты для дальнейшей сборки чанков.
                                          +2
                                          Недели 2-ве назад видел эту статью в песочнице, код был в ошибках. Скачивалась всегда 1-ая песня под разными именами. Код себе я тогда взял и поправил ошибки… )

                                          UP ошибка то осталась: audio = vk_audio.get(owner_id=my_id)[0]
                                            0
                                            А можно нам исправленную версию?
                                              +1
                                                +1
                                                Спасибо, работает! Единственное, для юниксов пришлось сделать следующее, чтобы убрать слеши из имени файла:
                                                diff --git a/src/main.py b/src/main.py
                                                index bfadfa5..b09d4c7 100755
                                                --- a/src/main.py
                                                +++ b/src/main.py
                                                @@ -7,6 +7,7 @@ import requests
                                                 from time import time
                                                 import vk_api
                                                 from vk_api import audio
                                                +import re
                                                
                                                 class vkMusicDownloader():
                                                
                                                @@ -94,6 +95,7 @@ class vkMusicDownloader():
                                                             # собственно циклом загружаем нашу музыку
                                                             for i in audio:
                                                                 fileM = "{} - {}.mp3".format(i["artist"], i["title"])
                                                +                fileM = re.sub('/', '_', fileM)
                                                                 try:
                                                                     if os.path.isfile(fileM) :
                                                                         print("{} Уже скачен: {}.".format(index, fileM))
                                                


                                                Нулевой байт убирать смысла не вижу (у меня ни одной записи с ним в имени не нашлось :-). Для других ОС будут инвалидными другие символы, но, думаю, убранный слеш покроет 99 % случаев ошибок записи файлов из-за неверных символов в имени.
                                                  0
                                                  Поправил.

                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                          Самое читаемое