Введение
Добрый день, давно я ничего не писал - много было причин. За это время накопилось достаточно большое количество материала и я постараюсь его сюда выставить как можно полнее. Сегодня я хочу рассказать о работе с API контакта через Python, а примером послужит - скачивание стены журнала Евгений Онегин. Глава 11. Если вам лень читать как я всё это делал, а охота поскорее скачать свою стену, переходите сразу к разделу "Отчёт и тестирование".
Подготовка
Первое, что нужно сделать - скачать АПИ и зарегистрировать приложение, получив его идентификатор ( если вам не охота регистрировать своё приложения и получать его идентификатор, то просто пропустите этот шаг - по умолчанию программа подставит мой идентификатор приложения) . АПИ доступно по этому адресу https://pypi.python.org/pypi/vkontakte .Там есть инструкция по установке. Если-же не получается по ней, то скачайте файлы, разархивируйте, в терминале выполните (Linux)[alex@archalex vkontakte]$ sudo python2 ./setup.py install [alex@archalex vkontakte]$ python2 Python 2.7.5 (default, May 12 2013, 12:00:47) [GCC 4.8.0 20130502 (prerelease)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import vkontakte
После установки мы можем пользоваться функционалом этой библиотеки, однако с некоторых пор стало обязательным создание окна браузера, чтоб пользователь там ввёл свои логин и пароль, и только потом мы получили token_id для работы с API питона. Чтоб не лезть в эти дебри можно прикинуться окном броузера, фиктивно ввести туда валидные логин и пароль и получить свой законный токен. В этой статье рассказано более подробно про этот маленький хак. В результате статьи был создан класс авторизации, в который сообщается логин, пароль, идентификатор приложения, запрашиваемые права. Я немного подредактировал этот файл, так как он не работал. Скачать мой вариант можно тут.
Первые шаги
Для первого примера давайте сделаем простую авторизацию с неким запросом.#!/usr/bin/env python3 # -*- coding: utf-8 -*- import vk_auth import vkontakte def main(): (token,user_id) = vk_auth.auth('$EMAIL', '$PASSWD', '$ID_APP', '$SOPE') vk = vkontakte.API(token=token) print "Hello vk API , server time is ",vk.getServerTime() return 0 if __name__ == '__main__': main()
Ничего сложного - импортируем класс авторизации, импортируем библиотеку c API, получаем токен и идентификатор, используем АПИ-шные функции.
Пример
Теперь пришло время к массивному, серьёзному, а главное практичному примеру - скачивание стены Вконтакте. Идея скачивания стены зародилась из острой необходимости. Паблик Вконтакте организован очень удобно, за одним исключением - старую информацию на стене невозможно найти. Родилась идея - скачать всю стену из паблика и разбить её на несколько HTML фалов для лучшей скорости доступа.Перед началом работы нужно поставить грамотное техническое задание.
Техническое задание
Скрипт или набор скриптов для скачивания стены из социальной сети Вконтакте.Входные данные:
- Имя пользователя
- Пароль
- Идентификатор группы\паблика\страницы
- Количество постов для скачивания
- Начиная с какого сообщения скачивать
- На сколько HTML блоков разбивать
Реализация
Обобщённый алгоритм работы программы |
Набросаем пока такой код. Я использовал библиотеку OptionParser для парсинга параметров - она идёт из коробки и о-о-очень удобная. Следующий шаг - организация разбивки на тома. Чуть изменив старый код, мы получаем алгоритм, который разобьёт информацию на необходимое количество томов. Вот код. Далее необходимо использовать vk API и загружать посты из Контакта. Покурив документацию, я быстро нашел необходимую функцию . Апи контакта позволяет отправлять JSON запросы и получать JSON ответы. Используемой нами vkAPI инкапсулирует JSON запросы в список переменных. Не могу сказать, что это очень удобно, но я не нашел другого способа. Для запроса постов со стены определённой группы нам необходимо выполнить следующий код
posts = vk.get('wall.get', owner_id=owner_id,offset=offset,count=count)
После выполнения этого кода в posts запишется count постов со смещением offset. Стоит обратить внимание, что на количество скачиваемых постов есть ограничение и, если я не ошибаюсь, оно равно 100. Я взял значение 50, как оптимальное количество скачиваемых постов за 1 итерацию цикла. В прошлом листинге я выделил 2 функции: скачивание постов get_posts() и форматирование в HTML posts_to_html(), и поставил на них заглушки. Теперь я заполняю get_posts() кодом, который привёл выше. В техническом задании ещё требовалось скачать все комментарии к коду. Я разделил функцию скачивания постов и оформления их в html, а значит скачивание комментариев должно производиться вместе со скачиванием постов и записываться в одну переменную. Благо, JSON - формат динамический и его несложно дополнить одним полем, в котором будут храниться комментарии.
В документации находим функцию скачивания комментариев и реализуем. Функцию скачивания поста с комментариями разобьём на 2 функции - скачивание поста и скачивание комментариев к нему. Получаем вот такой код:
def get_comments(vk,owner_id,post_id,count): size = 50 comments = [] if(count<size): com = vk.get('wall.getComments', owner_id=owner_id,post_id=post_id,count=count) comments += com[1:] else: offset = 0 while(offset<count): com = vk.get('wall.getComments', owner_id=owner_id,post_id=post_id,count=size,offset=offset) comments += com[1:] offset+=size return comments def get_posts(vk,owner_id,offset,count): posts = vk.get('wall.get', owner_id=owner_id,offset=offset,count=count) for i in range(1,len(posts)): cur = posts[i] coments_count = posts[i]['comments']['count'] if(coments_count>0): post_id = posts[i]['id'] comments = get_comments(vk,owner_id,post_id,coments_count) posts[i]['comments']['data']=comments print len(comments),coments_count return posts
Теперь функция get_posts вернёт посты, в которых в поле comments.data будут храниться комментарии к ним.
На этом работа со скачиванием данных закончена.Необходимо ещё из этой всей канители сформировать красивые HTML странички. Скажу честно - для меня это самый адский труд. Но других путей нет - вперёд.
Формирование HTML страниц.
Для формирования я написал целый ряд шаблонов, в которые потом буду вставлять данные из JSON. Я не буду их всех здесь приводить. Просто дам ссылку на готовый проект. Функция, формирования HTML вышла огромная и некрасивая.Если будут люди - профи в HTML, буду им благодарен за более благоприятный дизайн, который более схож с Vk.Скачивание Данных.
Одним из пунктов ТЗ был удобный локальный просмотр. В данный момент все ссылки на картинки и аудиозаписи - абсолютные (то есть ведут в интернет) а нужно чтоб они лежали рядом и при отсутствии интернета можно было и картинку посмотреть и музычку прослушать. Можно это сделать питоном через жуткую Караганду. Но я предпочёл вариант поэлегантней - wget. Эта стандартная линуксовская интрнето-качалка и она умеет скачивать web-страницу с информацией на ней.Чтобы скормить HTML-ки нужно ещё почитать man wget и найти несколько необходимых ключей.Получился вот такой скрипт:
$wget -B -k -r -l0 --force-html -i name-*.html -P ./data
Одно только НО - wget скачает всю информацию, но не преобразует ссылки.Может, конечно, он это и умеет делать, но я не нашел как. Поэтому в питоне мы напишем преобразование от любого контактовского адреса хранения фото\аудио в ссылку на рядом лежащую папку {ИмяГруппы}_{НомерТома}data. Я умышленно разделил имя группы и номер тома, чтобы можно было переместить за собой только один том и не тащить за собой несчётное количество информации, которая не используется.
def convert_lincs(txt,folder,recurce = 0): out = re.sub("http://",folder,txt) return out
Как видите, благодаря мощи регулярных выражений эту задачу мы решили в 1 строку.
Ещё я столкнулся с проблемой постоянно прерывающийся связи (у меня плохой интернет) и отказа со стороны Контакта из-за высокой частоты запросов. Эти 2 фактора периодически валили программу, поэтому я сделал отказоустойчивые обёртки для всех методов обращения в интернет, которые использовал. Получилось вот так:
def wall_getComments(vk,owner_id,post_id,count,recurce = 0): if (recurce ==20): return [] try: com = vk.get('wall.getComments', owner_id=owner_id,post_id=post_id,count=count) except: time.sleep(1) print "Error wall_getComments try ",recurce com = wall_getComments(vk,owner_id,post_id,count,recurce = recurce+1) return com def wall_get(vk,owner_id,offset,count,recurce = 0): if (recurce ==20): return [] try: res = vk.get('wall.get', owner_id=owner_id,offset=offset,count=count) except: time.sleep(1) print "Error wall_get try ",recurce res = wall_get(vk,owner_id,offset,count,recurce = recurce +1) return res def users_get(vk,uids,fields,recurce = 0): if (recurce ==20): return [] try: res = vk.get('users.get',uids=uids,fields="photo") except: time.sleep(1) print "Error users_get try ",recurce res = users_get(vk,uids,fields,recurce = recurce +1) return res def groups_getById(vk,group_ids,recurce = 0): if (recurce ==20): return [] try: res = vk.get('groups.getById', group_ids=group_ids) except: time.sleep(1) print "Error groups_getById try ",recurce res = groups_getById(vk,group_ids,recurce = recurce +1) return res
При отказе сети или ошибке получения данных я жду полсекунды и повторяю попытку.
Отчёт и тестирование
Результат работы я публикую на Github. Так , что вы можете присоединиться к проекту, взять мои наработки, добавить свои, переделать под свои нужды и просто развивать проект.И вот, наконец, тестирование. Обратите внимание на то, что я буду использовать Python2.X и не ругайтесь заранее, увидев что-то вроде этого при попытке запустить программу:
[alex@archalex WallCopy]$ python ./WallCopy.py File "./WallCopy.py", line 40 print "%s Failure"%"wall_getComments" ^ SyntaxError: invalid syntax
Программа выдаёт справку на аглицком если запустить её с ключём -h. Для ленивых перевожу на русский:
- -e EMAIL, --email=EMAIL Имеил для авторизации
- -p PASSWD, --passwd=PASSWD Пароль для авторизации, если вы его не задали в явном виде, программа попросит вас ввести его вовремя выполнения.Конечно пароль будет невидим.
- -i GID, --groupid=GID Идентификатор группы, стену которой необходимо скачать
- -c COUNT, --count=COUNT - количество постов, которые необходимо скачать. Если не задано - выкачивает все записи со стены.
- -f OFFSET, --offset=OFFSET - смещение, с которого необходимо начать скачивание, если не задано , то 0.
- -s SPLIT_NUM, --split=SPLIT_NUM - количество блоков, на которые разбить скачанные посты, если не задано, то 1
- -a APP_ID, --app_id=APP_ID - идентификатор приложения, которое вы получили зарегестрировавшись ( если не указано, то используется мой идентификатор приложения)
- -d DOWNLOAD, --download_all=DOWNLOAD - ключ, который определяет, следует ли загружать мультимедиа. Возможные значения:
- 0 - Не загружать ( по умолчанию)
- 1 - загружать после того, как основная информация загрузится
python2 ./WallCopy.py -e EMAIL -i GROUP_ID
При этом загрузятся все записи со стены с нулевым смещением, но ссылки останутся абсолютными. Пароль программа спросит в интерактивном режиме, а остальные опции примут значения по умолчанию.
Расширенный пример использования:
При этом загрузится 1000 постов со смещением на 500 и разобьётся на 8 томов, и картинки с музыкой загрузятся на компьютер.
На данном этапе есть много вещей, которые программа не поддерживает, а хотелось бы:
- Загрузка только фото и игнорирование музыки;
- Загрузка документов;
- Загрузка репостов;
- Более похожий дизайн на Vk;
- Превью Видео;
- Требование Linux ( wget )
- Поддержка только Python 2.X
- Использование ООП
Спасибо за полезный материал!
ОтветитьУдалитьСкажите, насколько сложно запустить данные скрипты из-под Windows?
Конечно ! Необходимо скачать Питон с официального сайта и скачать vkontakte от сюда. https://pypi.python.org/pypi/vkontakte
УдалитьСпасибо за ответ. Правильно ли я понимаю, что в строке:
Удалить(token,user_id) = vk_auth.auth('$EMAIL', '$PASSWD', '$ID_APP', '$SOPE')
надо подставить свой e-mail, пароль, id приложения и защищённый ключ приложения?
Да. Если вы беспокоитесь, что ваши данные утекут, то можете более подробно посмотреть в код модуля vk_auth. Ничего такого там нет. Он занимается эмуляцией веб-страницы авторизации Вконтакте, в которую вводит значения, что вы в него передали.
Удалитьсори , если я правильно понял , он каждый пост целиком сохраняет (текст с картинкой)?
УдалитьПрошу прощения, но, для меня это дико сложно, а стену очень необходимо сохранить. Могли бы вы подсказать, скачал я питон http://www.python.org/ftp/python/2.7.6/python-2.7.6.msi
ОтветитьУдалитьРазархивировал vkontakte от сюда. https://pypi.python.org/pypi/vkontakte
А что дальше?
а приложение качает все записи со стены или только те, которые запостили админы группы?
ОтветитьУдалитьНе совсем понятно, что именно и как именно сохраняет приложение.
ОтветитьУдалитьМожно пример?
Программа загружает всё это в HTML файлы( или файл, если ключ s указан в 1)
ОтветитьУдалитьПрорекламирую свой модуль доступа к API vk.com: https://github.com/dimka665/vk
ОтветитьУдалитьРеализована авторизация. Работает на Python2.7, Python3.3
!!!
ОтветитьУдалитьНе АПИ, а SDK для API
ОтветитьУдалитьХотел бы так же поделиться своими наработками для vk:
ОтветитьУдалитьhttps://github.com/prawn-cake/vk-requests - это заметно улучшенный fork проекта dimka665/vk c множеством баг и дизайн фиксов
Вконтакте это огромная пллщадь для заработка и рекламы, лучше для работы использовать отдельные страницы. Кстати, если нету дополнительных номеров для регистрации страниц вк, то вот https://sms-acktiwator.ru/ вам в помощь, там смски можно получить для любых соц.сетей.
ОтветитьУдалить