Для лингвистического исследования мне понадобился корпус прямой речи, порожденной одним человеком. Я решил, что для начала удобнее всего использовать собственную переписку в ВК. Это статья о том, как скачать все сообщения, которые Вы когда-либо отправляли своим друзьям, используя программу на 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/
Добавить комментарий