Экспорт фотоальбомов из ВКонтакта

    Преамбула


    Дело было вечером, делать было нечего и тут мне в голову пришла мысль: «Как же мне выгрузить все фотографии из ВКонтакта на компьютер?» Недолго думая, я написал утилиту для этого и решил поделиться с общественностью, возможно я не один такой.

    Поехали


    В качестве инструмента для работы с API, путём кратких поисков, была выбрана библиотека vk_api. Для работы с сетью она использует Requests, поэтому и эта библиотека требуется для запуска.

    Пощупать


    GitHub Pages проекта


    Само приложение получилось крайне простым и небольшим, но с задачей своей справляется. Весь код уместился в одном единственном файле.

    Исходный код
    #!/usr/bin/env python
    
    """
        :mod:`vkporter`
        ~~~~~~~~~~~~~~~
    
        A micro tool for export photo albums from `vk.com <https://vk.com>`_.
         It's based on `VK_API <https://github.com/python273/vk_api>`_
         by Kirill Python <mikeking568@gmail.com>,
         `Requests <python-requests.org>`_
         and `ProgressBar <https://code.google.com/p/python-progressbar/>`_.
    
        :copyright: (c) 2013 by Andrey Maksimov.
        :license: BSD, see LICENSE for more details.
    """
    
    __author__ = 'Andrey Maksimov <meamka@me.com>'
    __date__ = '09.03.13'
    __version__ = '0.1.1'
    
    import argparse
    import datetime
    from getpass import getpass
    import os
    import time
    import sys
    
    try:
        import requests
    except ImportError:
        print("Cannot find 'requests' module. Please install it and try again.")
        sys.exit(0)
    
    try:
        from vk_api import VkApi
    except ImportError:
        print("Cannot find 'vk_api' module. Please install it and try again.")
        sys.exit(0)
    
    
    def connect(login, password):
        """Initialize connection with `vk.com <https://vk.com>`_ and try to authorize user with given credentials.
    
        :param login: user login e. g. email, phone number
        :type login: str
        :param password: user password
        :type password: str
    
        :return: :mod:`vk_api.vk_api.VkApi` connection
        :rtype: :mod:`VkApi`
        """
        return VkApi(login, password)
    
    
    def get_albums(connection):
        """Get albums list for currently authorized user.
    
        :param connection: :class:`vk_api.vk_api.VkApi` connection
        :type connection: :class:`vk_api.vk_api.VkApi`
    
        :return: list of photo albums or ``None``
        :rtype: list
        """
        try:
            return connection.method('photos.getAlbums')
        except Exception as e:
            print(e)
            return None
    
    
    def get_photos(connection, album_id):
        """Get photos list for selected album.
    
        :param connection: :class:`vk_api.vk_api.VkApi` connection
        :type connection: :class:`vk_api.vk_api.VkApi`
        :param album_id: album identifier returned by :func:`get_albums`
        :type album_id: int
    
        :return: list of photo albums or ``None``
        :rtype: list
        """
        try:
            return connection.method('photos.get', {'aid': album_id})
        except Exception as e:
            print(e)
            return None
    
    
    def download(photo, output):
        """Download photo
    
        :param photo:
        """
        url = photo.get('src_xxxbig') or photo.get('src_xxbig') or photo.get('src_xbig') or photo.get('src_big')
    
        r = requests.get(url)
        title = photo['pid']
        with open(os.path.join(output, '%s.jpg' % title), 'wb') as f:
            for buf in r.iter_content(1024):
                if buf:
                    f.write(buf)
    
    
    def sizeof_fmt(num):
        """Small function to format numbered size to human readable string
    
        :param num: bytes to format
        :type num: int
    
        :return: human readable size
        """
        for x in ['bytes', 'KB', 'MB', 'GB', 'TB']:
            if num < 1024.0:
                return "%3.1f %s" % (num, x)
            num /= 1024.0
    
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser(description='', version='%(prog)s ' + __version__)
        parser.add_argument('username', help='vk.com username')
        # parser.add_argument('password', help='vk.com username password')
        parser.add_argument('-o', '--output', help='output path to store photos',
                            default=os.path.abspath(os.path.join(os.path.dirname(__file__), 'exported')))
    
        args = parser.parse_args()
    
        # expand user path if necessary
        if args.output.startswith('~'):
            args.output = os.path.expanduser(args.output)
    
        start_time = datetime.datetime.now()
        try:
            password = getpass("Password: ")
    
            # Initialize vk.com connection
            connection = connect(args.username, password)
    
            # Request list of photo albums
            albums = get_albums(connection)
            print("Found %s album%s:" % (len(albums), 's' if len(albums) > 1 else ''))
            ix = 0
            for album in albums:
                print('%3d. %-40s %4s item%s' % (
                ix + 1, album['title'], album['size'], 's' if int(album['size']) > 1 else ''))
                ix += 1
    
            # Sleep to prevent max request count
            time.sleep(1)
    
            if not os.path.exists(args.output):
                os.makedirs(args.output)
    
            for album in albums:
                response = get_photos(connection, album['aid'])
                output = os.path.join(args.output, album['title'])
                if not os.path.exists(output):
                    os.makedirs(output)
    
                processed = 0
    
                for photo in response:
                    percent = round(float(processed) / float(len(response)) * 100, 2)
                    sys.stdout.write(
                        "\rExporting %s... %s of %s (%2d%%)" % (album['title'], processed, len(response), percent))
                    sys.stdout.flush()
    
                    download(photo, output)
                    processed += 1
    
        except Exception as e:
            print(e)
            sys.exit(1)
    
        except KeyboardInterrupt:
            print('VKPorter exporting stopped by keyboard')
            sys.exit(0)
    
        finally:
            print("Done in %s" % (datetime.datetime.now() - start_time))
    
    



    Примеры


    По умолчанию экспорт происходит в папку ./exported
    $ ./vkporter.py username@vk.com
    

    Путь для экспорта можно указать при запуске
    $ ./vkporter.py -o ~/Documents/Exported username@vk.com
    


    Выглядит это примерно так:
    image

    Общение


    Буду благодарен на репорты о багах на github.com/amka/VKPorter/issues
    Метки:
    Поделиться публикацией
    Комментарии 38
    • +2
      Есть еще альтернативный вариант через расширение для фф addons.mozilla.org/ru/firefox/addon/vkontakte-photo/
      • 0
        спасибо кэп, работает. И даже вроде загружает в оригинальном разрешении. Буду теперь делать бэкапы в скрытых папках =)
        • 0
          Рад стараться.
      • +1
        SaveFrom умеет так делать, причём он кроссбраузерный.
        Однако, этот скрипт тоже может помочь, например, для загрузки фотографий на сервер.
        Сделали бы вы ещё такое для аудиозаписей…
        • 0
          Так можете посмотреть исходный код вот этого аддона: addons.mozilla.org/ru/firefox/addon/vkontakteru-downloader/?src=collection&collection_id=553245ed-e8b5-412f-8ce3-2ecef382c541
          Он успешно справляется с загрузкой медиаконтента.
          • 0
            Мне нужна загрузка на свой сервер, у которого из браузеров только links.
            • 0
              Так Вам и говорят, посмотрите исходный код :)
              • 0
                Посмотреть могут многие, а вот разобраться в нём — нет.
          • 0
            Для аудиозаписей, конечно, можно, но мне кажется там всё же надо больше интерактивности, которую я не очень люблю в консоли. То ли дело GUI приложение :)
            • +3
              Я писал скрипт на PHP (жду кучу минусов от хейтеров), состоящий из двух частей.
              Первая часть — авторизация. На неё заходишь с браузера, проходишь стандартную авторизацию oAuth, а скрипт кладёт рядом с собой файлик с токеном. При авторизации скрипт в scope прописывает параметр offline, который разрешает использовать токен при смене IP.
              Вторая часть — генератор. Её нужно вызвать из консоли, командой php <имяскрипта>. Она запрашивает аудиозаписи у того, кто залогинился, создаёт .sh файлик с командами для загрузки (wget) и выполняет его. После его работы в папочке будут лежать все мои аудиозаписи, названные нормальными именами (а не хэшами).
              Для чего всё это? Ответ прост, для радио. Для нон-стоп режима. Очень удобно, просто слил туда огромную базу треков, и слушаешь, например, на работе или в институте, где есть вай-фай.
              • +2
                sh с wget внутри? Вот это попахивает извращением) Почему не заюзали curl?
                Нормальные имена вытаскивали с помощью чего? Я так понимаю ID3 после скачивания проходили
                А самое главное — токен на своем компе нужно было получить, потом по ftp/sftp/scp заливали на сервер?
                • +1
                  Сервер стоит у меня дома, поэтому проблемы с совместимостью айпишников не было.
                  Нет, зачем. у меня все записи поименованы хорошо, брал имена из приходящего мне JSONа.
                  Почему не заюзал CURL? Потому что неохота менять 30-секундный лимит выполнения.
                  После того, как PHP выполнит своё грязное дело, просто запускаем .sh и уходим чаёвничать.
              • 0
                Amka, а что насчет VKMusic? Я пользуюсь этим приложением, если нужно скачать медиа контент из контакта. Очень радует его работа =) Возможно это то, что тебе подойдет ;)
                • 0
                  Я, в общем, не скачиваю оттуда музыку, окромя их официального клиента под смартфон. Это были мои размышления на пожелания namikiri
              • 0
                Скачиваете плейлист со всеми аудио, закидываете на сервер, на сервере с помощью aria2 закачиваете по списку в несколько потоков.
                • 0
                  вот запилил korzhyk.github.com/VKMusic/ krogin
                  • 0
                    Можно установить:

                    $ easy_install VKMusic

                    или

                    $ pip install VKMusic
                  • 0
                    Попробуйте пожалуйста saver.zamp3.ru/. Есть возможность синхронизации с Google Drive.
                  • 0
                    Алгоритм ранжирования на окне уже записали?
                    • +3
                      Подумал, что на скрине прогресс-бары разных потоков загрузки, ан нет — просто ПД))
                      • –3
                        На хабре недавно проскакивала прога Songo плеера вконтакте.
                        Как думаете стоит ли написать прогу на дельфи для прослушивания Музыки вконтакте онлайн ??
                        Есть Желающие помочь пишите в личку!!!

                        Жаль что хром не может сворачиватся в трей как Винамп…
                      • 0
                        Почему выбран такой тип оформления параметров?
                        :param num: bytes to format
                        :type num: int

                        :return: human readable size

                        Это какой-то стандарт?
                        • 0
                          Автор писал в IDEA (сужу по .gitignore), там по умолчанию reStructuredText для доков. Sphinx потом генерирует хорошую доку.
                          • +1
                            Писал в PyCharm, да, но у нас и в других проектах используется Sphinx, так что привычно было использовать его.
                        • 0
                          Да: docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#field-lists.
                          Еще есть epytext, мне он больше нравится: epydoc.sourceforge.net/manual-epytext.html#fields

                          UPD: это к комментарию выше.
                          Автору могу посоветовать использовать enumerate, не изменять аргументов функций и вообще почитать доку. Хотя код оформлен гламурненько =)
                          • +1
                            www.olexandr.org/blog/c-pervyj-blin.html
                            вот, в конце прошлого года пробовал писать свое первое приложение на шарпе
                            • +2
                              я когда-то написал экспорт всех заметок в Эвернот
                              • 0
                                Работает ли это сейчас?
                                На github.com есть?
                                • 0
                                  должно работать. оно как приложение вк сделано, но чтоб добавить в общественный каталог там нужны какие-то голоса

                                  на гитхабе нет
                            • 0
                              Еще бы кто-нибудь написал приложение для переноса альбомов из Вконтакте в Facebook. Был проект «Итутитам», но данный функционал там перестал работать.
                              • –1
                                Я все мечтаю написать скрипт по экспорту аудиозаписей со стены группы вконтакте (не из альбома, а именно со стены). Только что-то как-то никак не реализую мечту свою.
                                • 0
                                  есть люди которые многое делают с аудио — они плеер пишут meridianvk.com, думаю можно их спросить как
                                  • 0
                                    ну там только слушать можно… а мнеб на телефон залить)
                                • 0
                                  А как бы сохранить «фотографии со мной»?

                                  Вот как раз их хотелось бы сохранить, например, перед удалением странички.
                                  Заранее благодарю!
                                  • 0
                                    было дело, делал аналог в такой же связке по экспорту фоток из livejournal'а

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