Скачиваем историю переписки со всеми пользователями ВКонтакте с помощью Python

Для лингвистического исследования мне понадобился корпус прямой речи, порожденной одним человеком. Я решил, что для начала удобнее всего использовать собственную переписку в ВК. Это статья о том, как скачать все сообщения, которые Вы когда-либо отправляли своим друзьям, используя программу на Python и API ВКонтакте. Для работы с API будем использовать библиотеку vk.

Для работы с сайтом нужно создать приложение и авторизоваться с помощью токена. Этот процесс не представляет из себя ничего сложного и описан здесь и здесь.

Итак, токен получен. Импортируем необходимые библиотеки (time и re понадобятся нам позже), подключимся к нашему приложению и начнем работу.

import vk import time import re  session = vk.Session(access_token='your_token') vkapi = vk.API(session)

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

friends = vkapi('friends.get') # получение всего списка друзей для пользователя # friends = [1111111, 2222222, 33333333] # задаем друзей вручную 

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

def get_dialogs(user_id): 	dialogs = vkapi('messages.getDialogs', user_id=user_id) 	return dialogs

Такая функция возвращает «шапку» диалога с пользователем, id которого равен указанному user_id. Результат её работы выглядит приблизительно так:

[96, {'title': ' ... ', 'body': '', 'mid': 333333, 'read_state': 1, 'uid': 111111, 'date': 1490182267, 'fwd_messages': [{'date': 1490173134, 'body': 'Не, ну все это и так понятно, но нам же там жить.', 'uid': 222222}], 'out': 0}]

В полученном списке содержится количество сообщений (96) и данные последнего сообщения в диалоге. Теперь у нас есть всё необходимое, чтобы скачать нужные диалоги.

Основное неудобство состоит в том, что ВКонтакте позволяет делать максимум около трех запросов в секунду, поэтому после каждого запроса нужно какое-то время ждать. Для этого нам и нужна библиотека time. Самое маленькое время ожидания, которое мне удавалось поставить, чтобы не получить отказ через несколько операций — 0.3 секунды.

Другая сложность в том, что за один запрос можно скачать максимум 200 сообщений. С этим тоже придется бороться. Напишем функцию.

def get_history(friends, sleep_time=0.3): 	all_history = [] 	i = 0 	for friend in friends: 		friend_dialog = get_dialogs(friend) 		time.sleep(sleep_time) 		dialog_len = friend_dialog[0] 		friend_history = [] 		if dialog_len > 200: 			resid = dialog_len 			offset = 0 			while resid > 0: 				friend_history += vkapi('messages.getHistory',  					user_id=friend,  					count=200,  					offset=offset) 				time.sleep(sleep_time) 				resid -= 200 				offset += 200 				if resid > 0: 					print('--processing', friend, ':', resid,  						'of', dialog_len, 'messages left') 			all_history += friend_history 		i +=1 		print('processed', i, 'friends of', len(friends)) 	return all_history

Разберемся, что здесь происходит.

Мы проходим по списку друзей и получаем диалог с каждым из них. Рассматриваем длину диалога. Если диалог короче, чем 200 сообщений, просто переходим к следующему другу, если длиннее, то скачиваем первые 200 сообщений (аргумент count), добавляем их в историю сообщений для данного друга и рассчитываем, сколько еще сообщений осталось скачать (resid). До тех пор пока остаток больше 0, при каждой итерации увеличиваем аргумент offset, который позволяет задать отступ в количестве сообщений от конца диалога, на 200.

Из-за необходимости ожидания после каждого запроса программа работает довольно долго, поэтому я добавил вывод небольшого отчета о текущем шаге, чтобы понимать, что сейчас обрабатывается и сколько еще осталось.

N.B.: у метода messages.get есть аргумент out, с помощью которого можно попросить сервер отдавать только исходящие сообщения. Я решил не использовать его и выделить нужные мне сообщения уже после скачивания по следующим причинам: а) файл все равно придется очищать, т.к. сервер отдает каждое сообщение виде словаря, содержащего много технической информации и б) сообщения собеседников тоже могут представлять интерес для моего исследования.

Каждое скачанное сообщение является словарем и выглядит примерно вот так:
{'read_state': 1, 'date': 1354794668, 'body': 'Вот так!<br>Потому что тут модель вышла довольно непонятная.', 'uid': 111111, 'mid': 222222, 'from_id': 111111, 'out': 1}

Далее осталось только очистить результат и сохранить его в файл. Эта часть работы уже не относится к взаимодействию с VK API, поэтому я не буду останавливаться на ней подробно. Да и что тут рассказывать — просто выбираем нужные элементы (body) для нужного пользователя и с помощью re удаляем переносы строк, которые отмечены тегом <br>. Сохраняем все в файл.

Полностью код программы выглядит вот так:

import vk import time import re  session = vk.Session(access_token='your_token') vkapi = vk.API(session)  SELF_ID = 111111 SLEEP_TIME = 0.3  friends = vkapi('friends.get') # получение всего списка друзей для текущего пользователя  def get_dialogs(user_id): 	dialogs = vkapi('messages.getDialogs', user_id=user_id) 	return dialogs  def get_history(friends, sleep_time=0.3): 	all_history = [] 	i = 0 	for friend in friends: 		friend_dialog = get_dialogs(friend) 		time.sleep(sleep_time) 		dialog_len = friend_dialog[0] 		friend_history = [] 		if dialog_len > 200: 			resid = dialog_len 			offset = 0 			while resid > 0: 				friend_history += vkapi('messages.getHistory',  					user_id=friend,  					count=200,  					offset=offset) 				time.sleep(sleep_time) 				resid -= 200 				offset += 200 				if resid > 0: 					print('--processing', friend, ':', resid,  						'of', dialog_len, 'messages left') 			all_history += friend_history 		i +=1 		print('processed', i, 'friends of', len(friends)) 	return all_history  def get_messages_for_user(data, user_id): 	self_messages = [] 	for dialog in data: 		if type(dialog) == dict: 			if dialog['uid'] == user_id and dialog['from_id'] == user_id: 				m_text = re.sub("<br>", " ", dialog['body']) 				self_messages.append(m_text) 	print('Extracted', len(self_messages), 'messages in total') 	return self_messages  def save_to_file(data, file_name='output.txt'): 	with open(file_name, 'w', encoding='utf-8') as f: 	    print(data, file=f)  if __name__ == '__main__': 	all_history = get_history(friends, SLEEP_TIME) 	save_to_file(all_history, 'raw.txt')  	self_messages = get_messages_for_user(all_history, SELF_ID) 	save_to_file(self_messages, 'sm_corpus.txt')

На момент запуска программы у меня в ВК было 879 друзей. На их обработку потребовалось около 25 минут. Файл с необработанным результатом имел объем 74MB. После выделения текста только моих сообщений — 15MB. Всего сообщений в полученном корпусе — около 150 000, а их текст занимает 3707 страниц (в вордовском документе).

Надеюсь, моя статья окажется для кого-то полезной. Все методы, которые можно использовать для обращения к API ВК, детально описаны в разделе для разработчиков ВКонтакте.
ссылка на оригинал статьи https://habrahabr.ru/post/325368/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *