[Из песочницы] Скачиваем историю переписки со всеми пользователями ВКонтакте с помощью Python
Для работы с сайтом нужно создать приложение и авторизоваться с помощью токена. Этот процесс не представляет из себя ничего сложного и описан здесь и здесь.
Итак, токен получен. Импортируем необходимые библиотеки (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': 'Вот так!
Потому что тут модель вышла довольно непонятная.', 'uid': 111111, 'mid': 222222, 'from_id': 111111, 'out': 1}
Далее осталось только очистить результат и сохранить его в файл. Эта часть работы уже не относится к взаимодействию с VK API, поэтому я не буду останавливаться на ней подробно. Да и что тут рассказывать — просто выбираем нужные элементы (body) для нужного пользователя и с помощью re удаляем переносы строк, которые отмечены тегом
. Сохраняем все в файл.
Полностью код программы выглядит вот так:
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("
", " ", 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 ВК, детально описаны в разделе для разработчиков ВКонтакте.