{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Основы программирования в Python\n", "\n", "\n", "### Работа с API ВКонтакте: продолжение\n", "\n", "*Алла Тамбовцева, НИУ ВШЭ*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выполняем операции, необходимые для работы с API ВКонтакте (см. предыдущую лекцию):" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import vk" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "token = \"\" # введите Ваш токен\n", "session = vk.Session(access_token = token)\n", "api = vk.API(session)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Попробуем получить определенную информацию о пользователях, ту информацию, которая хранится в определенных полях (*fields*). Например, нас интересует пол пользователей, город проживания и данные по высшему образованию (институт/университет, факультет, отделение и прочее). \n", "\n", "Берем метод `users.get()` и указываем id интересующих нас пользователей. Если числовой id неизвестен, достаточно имени пользователя в том виде, в каком мы его видим в ссылке на страницу с профилем. В списке с id могут быть элементы разных типов, как числовые id, так и текстовые имена одновременно:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'id': 171544496,\n", " 'first_name': 'Наталья',\n", " 'last_name': 'Василёнок',\n", " 'is_closed': False,\n", " 'can_access_closed': True,\n", " 'sex': 1,\n", " 'city': {'id': 1, 'title': 'Москва'},\n", " 'universities': [{'id': 128,\n", " 'country': 1,\n", " 'city': 1,\n", " 'name': 'НИУ ВШЭ (ГУ-ВШЭ)',\n", " 'faculty': 475,\n", " 'faculty_name': 'Факультет социальных наук',\n", " 'chair': 2037672,\n", " 'chair_name': 'Политология',\n", " 'graduation': 2017,\n", " 'education_form': 'Очное отделение',\n", " 'education_status': 'Студентка (бакалавр)'}]},\n", " {'id': 20473269,\n", " 'first_name': 'Алла',\n", " 'last_name': 'Тамбовцева',\n", " 'is_closed': False,\n", " 'can_access_closed': True,\n", " 'sex': 1,\n", " 'city': {'id': 1, 'title': 'Москва'},\n", " 'universities': [{'id': 128,\n", " 'country': 1,\n", " 'city': 1,\n", " 'name': 'НИУ ВШЭ (ГУ-ВШЭ)',\n", " 'graduation': 2016,\n", " 'education_status': 'Студентка (бакалавр)'}]}]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "api.users.get(user_ids = ['navasilyonok', 20473269], \n", " v = 5.103, \n", " fields = ['sex', 'city', 'universities'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Некоторые методы работают только с числовыми id. Как его получить с помощью API на основе текстового имени пользователя? Просто – запросить данные по пользователю и из списка с основной информацией извлечь значение поля `'id'`, что выглядит как извлечение значения по ключу из словаря:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "20473269" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "id_ = api.users.get(user_ids = 'allatamb', v = 5.103)[0]['id']\n", "id_" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Один из методов, который работает только с числовыми id – метод для получения списка друзей:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# friends.get() вернет словарь\n", "# из него извлекаем список по ключу items\n", "\n", "friends = api.friends.get(user_id = id_, v = 5.103)['items']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выставим задержку по времени в 2 секунды, чтобы не делать частых запросов и не столкнуться с ошибкой, вызываемой ограничениями API по числу запросов в секунду, и запросим имена и фамилии друзей пользователя:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Валерий Окунев\n", "Егор Юрескул\n", "Антон Воробьев\n", "Любовь Сысоева\n", "Антон Клименков\n", "Dmitry Ekhilevskiy\n" ] }, { "ename": "KeyboardInterrupt", "evalue": "", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0muser\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mapi\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0musers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0muser_ids\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mv\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m5.103\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0muser\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'first_name'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0muser\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'last_name'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0msleep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mKeyboardInterrupt\u001b[0m: " ] } ], "source": [ "from time import sleep\n", "\n", "for f in friends:\n", " user = api.users.get(user_ids = f, v = 5.103)[0]\n", " print(user['first_name'], user['last_name'])\n", " sleep(2) \n", " \n", "# это надолго, прервала исполнение этой ячейки" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь от сбора информации перейдем к ее генерации. Напишем небольшого бота, который будет отвечать на сообщения сообщества. Для этого нам потребуется другая библиотека, `vk_api`, ее можно установить, как обычно:\n", "\n", " !pip install vk_api" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Из этой библиотеки нам понадобятся функции из модуля `longpoll`, которые позволяют формировать «долгий» запрос к API ВКонтакте. С «долгими» запросами вместо того, чтобы обращаться к API каждую минуту в рамках написанного нами цикла, мы сможем написать код, который будет обращаться к API, ждать долгое время, пока мы не прервем исполнение кода, отслеживать происходящие события и реагировать на них. Другими словами, наш код будет очень гибким: если ничего не происходит, бот бездействует, если появляется что-то новое – сообщение, пост на стене или что-то еще – он мгновенно реагирует и пишет ответ." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "import vk_api\n", "from vk_api.longpoll import VkLongPoll, VkEventType" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Чтобы написать бота, который будет отвечать на сообщения в сообществе, это сообщество сначала нужно создать. Заходим в раздел *Сообщества* на своей странице ВКонтакте, создаем новое сообщество. После создания заходим в *Управление*, далее – в раздел *Сообщения*. Заходим в подраздел *Настройки для бота* , включаем возможности бота. В разделе *Сообщения* также включаем сообщения сообщества, чтобы бот мог писать от его имени. \n", "\n", "Затем переходим к подразделу *Работа с API* в разделе *Настройки* и создаем ключ. Этот ключ будет нашим токеном доступа, когда мы будет подключать бота из Python. Копируем ключ доступа и сохраняем его в переменную:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "token = \"7d...\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Открываем сессию работы с API и создаем объект `longpoll` для долгих запросов:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "vk_session = vk_api.VkApi(token=token)\n", "longpoll = VkLongPoll(vk_session)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Создаем объект `vk`, который будет обеспечивать нам действия ВКонтакте:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vk = vk_session.get_api()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь напишем специфический код. \n", "\n", "Возьмем функцию `longpoll.listen()`, которая будет отслеживать события («прислушиваться»), и запустим цикл, который будет перебирать все новые события в сообществе (`event`) и, если это событие является новым текстовым сообщением (`VkEventType.MESSAGE_NEW` и `event.text`), проверять, что в этом сообщении написано, и отвечать на него. " ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "for event in longpoll.listen():\n", " if event.type == VkEventType.MESSAGE_NEW and event.to_me and event.text:\n", " \n", " # если текст Привет! или привет\n", " # сообщаем погоду\n", " # делаем это, если сообщение от пользователя – from user\n", " if event.text == 'Привет!' or event.text == 'привет':\n", " if event.from_user:\n", " vk.messages.send(\n", " user_id=event.user_id,\n", " message='Привет! В Москве солнечно, ясно, -2',\n", " random_id=123456)\n", " # если текст пока \n", " # пишем Пока!\n", " if event.text == 'пока':\n", " if event.from_user:\n", " vk.messages.send(\n", " user_id=event.user_id,\n", " message='Пока!',\n", " random_id=123457)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Конечно, данный пример учебный, но код выше можно всячески модифицировать, формулировать сложные условия, добавлять функции, которые будут действительно узнавать погоду на каком-нибудь сайте и отправлять ее пользователю и многое другое.\n", "\n", "**Важно:** всегда указывайте в `messages.send()` аргумент `random_id`, без него метод работать не будет. Это случайный набор цифр, но он необходим. Про него можно почитать в документации API ВКонтакте в разделе, посвященном классу методов `messages`.\n", "\n", "*Примечание: код с `longpoll` выше заимствован [отсюда](https://habr.com/ru/post/427691/).*" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.4" } }, "nbformat": 4, "nbformat_minor": 2 }