Лабораторна робота №4. API ВКонтакте

Ця робота присвячена взаємодії з API ВКонтакте .

Зауваження: Якщо ви не знайомі з терміном "API", то рекомендую прочитати статтю: What is an API? In English, please.

Щоб почати працювати з API від вас вимагається зареєструвати новий додаток. Для цього треба зайти на форму створення нового Standalone додатки com/editapp?act=create> https://vk.com/editapp?act=create і дотримуйтесь інструкцій. Вашому додатку буде призначений ідентифікатор, який буде потрібно для виконання роботи.

Запити до API ВКонтакте мають наступний формат ( з документації ):

https://api.vk.com/method/METHOD_NAME?PARAMETERS&access_token=ACCESS_TOKEN&v=V

де:

  • METHOD_NAME - це назва методу API, до якого Ви хочете звернутися.
  • PARAMETERS - вхідні параметри відповідного методу API, послідовність пар name = value, об'єднаних амперсандом &.
  • ACCESSS_TOKEN - ключ доступу.
  • V - використовувана версія API (зараз 5.53).

Наприклад, щоб отримати список друзів, із зазначенням їх статі, потрібно виконати наступний запит:

https://api.vk.com/method/friends.get?fields=sex&access_token=0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5bc866455623bd560732ab&v=5.53

Увага: Токен доступу несправжній, тому цей запит не працюватиме.

Щоб отримати токен доступу ви можете скористатися написаним для вас скриптом access_token.py наступним чином:

$ Python access_token.py YOUR_CLIENT_ID -s friends, messages

де замість YOUR_CLIENT_ID необхідно підставити ідентифікатор вашого додатка.

Після виконання команди відкриється нова вкладка браузера, з адресного рядка якого вам необхідно скопіювати токен доступу.

Увага: На цьому етапі ви можете повторити раніше представлений приклад запиту, щоб переконатися, що ви робите все вірно.

Далі наведено вміст файлу access_token.py:

import webbrowser import argparse def get_access_token (client_id, scope): assert isinstance (client_id, int), 'clinet_id must be positive integer' assert isinstance (scope, str), 'scope must be string' assert client_id> 0, 'clinet_id must be positive integer 'url = "" "\ https://oauth.vk.com/authorize?client_id= {client_id} & \ redirect_uri = https: //oauth.vk.com/blank.hmtl& \ scope = {scope} & \ & response_type = token & \ display = page \ "" ".replace (" "," ") .format (client_id = client_id, scope = scope) webbrowser.open_new_tab (url) if __name__ ==" __main__ ": parser = argparse. ArgumentParser () parser.add_argument ( "client_id", help = "Application Id", type = int) parser.add_argument ( "-s", dest = "scope", help = "Permissions bit mask", type = str, default = "", required = False) args = parser.parse_args () get_access_token (args.client_id, args.scope)

Завдання №1. Потрібно написати функцію прогнозування віку користувача за віком його друзів.

def get_friends (user_id, fields): "" "Returns a list of user IDs or detailed information about a user's friends" "" assert isinstance (user_id, int), "user_id must be positive integer" assert isinstance (fields, str), "fields must be string" assert user_id> 0, "user_id must be positive integer" pass

Для виконання цього завдання потрібно отримати список всіх друзів для зазначеного користувача, відфільтрувати тих у кого вік не зазначений або вказані тільки день і місяць народження.

Для виконання запитів до API ми будемо використовувати бібліотеку requests :

$ Pip install requests

Список користувачів можна отримати за допомогою методу friends.get . Нижче наведено приклад звернення до цього методу для отримання списку всіх друзів зазначеного користувача:

domain = "https://api.vk.com/method" access_token = user_id = query_params = { 'domain': domain, 'access_token': access_token, 'user_id': user_id, 'fields': 'sex'} query = "{domain} /friends.get?access_token= {access_token} & user_id = {user_id} & fields = {fields} & v = 5.53" .format (** query_params) response = requests.get (query)

Функція requests.get виконує GET запит і повертає об'єкт Response, який являє собою відповідь сервера на посланий нами запит.

Об'єкт Response має безліч атрибутів:

response. <tab> response.apparent_encoding response.history response.raise_for_status response.close response.is_permanent_redirect response.raw response.connection response.is_redirect response.reason response.content response.iter_content response.request response.cookies response.iter_lines response.status_code response.elapsed response.json response.text response.encoding response.links response.url response.headers response.ok

Нас буде цікавити тільки метод response.json, який повертає JSON об'єкт:

response.json () { 'response': { 'count': 136, 'items': [{ 'first_name': 'Drake', 'id': 1234567, 'last_name': 'Wayne', 'online': 0 , 'sex': 1}, { 'first_name': 'Gracie' 'id': 7654321, 'last_name': 'Habbard', 'online': 0, 'sex': 0}, ... response.json ( ) [ 'response'] [ 'count'] 136 response.json () [ 'response'] [ 'items'] [0] [ 'first_name'] 'Drake'

Поле count містить число записів, а items список словників з інформацією по кожному користувачеві.

Виконуючи запити ми не можемо бути впевнені, що не виникне помилок. Можуть бути різні ситуації, наприклад:

  • є неполадки в мережі;
  • віддалений сервер з якоїсь причини не може обробити запит;
  • ми занадто довго чекаємо відповідь від сервера.

У таких випадках необхідно спробувати повторити запит. При цьому повторні запити бажано надсилати не через константні проміжки часу, а за алгоритмом експоненційної затримки.

Напишіть функцію get (), яка буде виконувати GET-запит до вказаної адреси, а при необхідності повторювати запит вказане число раз за алгоритмом експоненційної затримки:

def get (url, params = {}, timeout = 5, max_retries = 5, backoff_factor = 0.3): "" "Виконати GET-запит: param url: адреса, на який необхідно виконати запит: param params: параметри запиту: param timeout : максимальний час очікування відповіді від сервера: param max_retries: максимальне число повторних запитів: param backoff_factor: коефіцієнт експоненціального наростання затримки "" "pass get (" https://httpbin.org/get ") <Response [200]> get (" https://httpbin.org/delay/2 ", timeout = 1) ReadTimeout: HTTPSConnectionPool (host = 'httpbin.org', port = 443): Read timed out. (Read timeout = 1) get ( "https://httpbin.org/status/500") HTTPError: 500 Server Error: INTERNAL SERVER ERROR for url: https://httpbin.org/status/ 500 get ( "https: //noname.com ", timeout = 1) ConnectionError: HTTPSConnectionPool (host = 'noname.com', port = 443): Max retries exceeded with url: /

На поточний момент ви повинні заповнити тіло функції get_friends так, щоб вона повертала список друзів для зазначеного користувача. Аргумент fields вдає із себе рядок, в якій через кому вказуються які поля необхідно отримати по кожному користувачеві.

Тепер ми можемо написати функцію age_predict для "наївного" прогнозування віку користувача з ідентифікатором user_id (під "наївним" прогнозуванням мається на увазі обчислення середнього арифметичного або медіани):

def age_predict (user_id): "" ">>> age_predict (???) ???" "" assert isinstance (user_id, int), "user_id must be positive integer" assert user_id> 0, "user_id must be positive integer "pass

Зауваження: Так як дата народження користувача може бути не вказана або вказані тільки день і місяць, то для обробки таких ситуацій ви можете використовувати конструкцію try ... except, де except буде містити тільки pass.

Завдання №2: Потрібно написати функцію, яка б виводила графік частоти листування з зазначеним користувачем.

Давайте почнемо з того, що отримаємо всю або частину листування з зазначеним користувачем. Для цього вам буде потрібно реалізувати метод API messages.getHistory за аналогією з тим, як ви реалізовували отримання списку друзів користувача:

def messages_get_history (user_id, offset = 0, count = 20): assert isinstance (user_id, int), "user_id must be positive integer" assert user_id> 0, "user_id must be positive integer" assert isinstance (offset, int), " offset must be positive integer "assert offset> = 0," user_id must be positive integer "assert count> = 0," user_id must be positive integer "pass

Коментар: У реалізації функції messages_get_history потрібно враховувати, що API ВКонтакте встановлює обмеження на кількість запитів в одну секунду (на сьогоднішній день це три запиту). Число повідомлень, яке ви можете отримати за одне запит - 200.

Нижче наведено приклад використання функції messages_get_history:

user_id = history = messages_get_history (user_id) from pprint import pprint as pp message = history [ 'response'] [ 'items'] [0] pp (message) { 'body': 'Це текст повідомлення.' , 'Date': 1474811631, 'from_id': USER_ID_HERE, 'id': 168989, 'out': 0, 'read_state': 1, 'user_id': USER_ID_HERE}

Кожне повідомлення містить наступні поля:

  • body - текст повідомлення;
  • date - дата відправки повідомлення в форматі unixtime ;
  • from_id - ідентифікатор автора повідомлення;
  • id - ідентифікатор повідомлення;
  • out - тип повідомлення (0 - отримане, 1 - відправлене не повертається для пересланих повідомлень);
  • read_state - статус повідомлення (0 - не прочитано, 1 - прочитано, що не повертається для пересланих повідомлень);
  • user_id - ідентифікатор користувача, в діалозі з яким знаходиться повідомлення.

Нас цікавить поле date, яке представляє дату відправки повідомлення в форматі unixtime. Щоб змінити формат дати уявлення можна скористатися функцією strftime з модуля datetime:

from datetime import datetime date = datetime.fromtimestamp (message [ 'date']). strftime ( "% Y-% m-% d") '2016-09-25'

Формат представлення вказується у вигляді рядки форматування , Наприклад,% Y-% m-% d - рік, місяць і день, відповідно.

На даний момент вашим завданням є написати функцію count_dates_from_messages, яка повертає два списки: список дат і список частоти кожної дати, а приймає список повідомлень:

def count_dates_from_messages (messages): pass

Зауваження: При великій кількості повідомлень ви їх можете розбити по тижнями або місяцями.

Далі ми скористаємося сервісом Plot.ly , Який надає API для малювання графіків. Запит містить інформацію про точках, які потрібно відобразити на графіку. Вам потрібно зареєструватися та отримати ключ доступу (API KEY). Для більш простого взаємодії з Plot.ly ми скористаємося готовим модулем (існують рішення і для інших мов):

$ Pip3 install plotly

Перед початком його використання потрібно провести попередню настройку , Вказавши ключ доступу та ім'я користувача:

import plotly plotly.tools.set_credentials_file (username = 'YOUR_USER_NAME', api_key = 'YOUR_API_KEY')

Нижче наведено приклад побудови графіка, де змінна x містить дати, а y - кількість повідомлень в цей день:

import plotly.plotly as py import plotly.graph_objs as go from datetime import datetime x = [datetime (year = 2016, month = 09, day = 23), datetime (year = 2016, month = 09, day = 24), datetime (year = 2016, month = 09, day = 25)] data = [go.Scatter (x = x, y = [142, 50, 8])] py.iplot (data)

Створений графік ви можете знайти в своєму профілі :

Завдання №3: Потрібно написати функцію get_network (), яка для зазначеного списку користувачів users_ids будує граф і представляє його або у вигляді матриці суміжності (as_edgelist = False), або у вигляді списку ребер (as_edgelist = True). В отриманому графі необхідно виділити спільноти і візуалізувати результат.

def get_network (users_ids, as_edgelist = True): "" "Building a friend graph for an arbitrary list of users" "" pass

Пошук спільнот на графі (community detection) є добре вивченою завданням, а ряд найбільш відомих алгоритмів реалізований в бібліотеці igraph.

$ Pip install python-igraph $ pip install numpy $ pip install cairocffi $ brew install cairo from igraph import Graph, plot import numpy as np vertices = [i for i in range (7)] edges = [(0, 2), ( 0, 1), (0, 3), (1, 0), (1, 2), (1, 3), (2, 0), (2, 1), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (4, 5), (4, 6), (5, 4), (5, 6), (6, 4) , (6, 5)] g = Graph (vertex_attrs = { "label": vertices}, edges = edges, directed = False) N = len (vertices) visual_style = {} visual_style [ "layout"] = g.layout_fruchterman_reingold ( maxiter = 1000, area = N ** 3, repulserad = N ** 3) plot (g, ** visual_style)

g.simplify (multiple = True, loops = True)

communities = g.community_edge_betweenness (directed = False) clusters = communities.as_clustering () print (clusters) Clustering with 7 elements and 2 clusters [0] 0, 1, 2, 3 [1] 4, 5, 6 pal = igraph. drawing.colors.ClusterColoringPalette (len (clusters)) g.vs [ 'color'] = pal.get_many (clusters.membership)

Com/editapp?
Com/method/METHOD_NAME?
Get?
Com/authorize?
Get?

Дополнительная информация

rss
Карта