Когда в адресной книге Google набирается несколько тысяч клиентов, становится тяжеловато ворочать этим объемным списком. Импорт/экспорт в странички Excel становится неудобным; кроме того появляются CRM-подобные приложения, в которых хотелось бы интегрировать самые разные базы данных, в том числе и контакты Google. Значит, пришло время доступиться к нашим контактам через консольные приложения, используя Google Contacts API.
Получение списка контактов
Используя API, мы можем не только получить список контактов, но также и видоизменять и дополнять его. В простейшем случае, первые 150 записей из адресной книги можно вытащить так, вбив это в адресную строку браузера:
1 2 |
https://www.google.com/m8/feeds/contacts/default/full?access_token=ACCESS_TOKEN &alt=json&max-results=150&start-index=1 |
Можно догадаться, что данные мы получим в формате json, что понятно, поскольку каждой записи в адресной книге соответствует куча полей, также понятно сколько записей будет выведено. И поскольку здесь нет ни нашего логина, ни пароля, можно предположить, что ACCESS_TOKEN — это та самая строка, которая обеспечивает авторизацию. Чтобы понять, как эта авторизация работает, мне в свое время пришлось выполнить несколько танцев с бубном. В этой статье делюсь результатами своих изысканий.
Регистрируемся в Google
Предполагаю, что у вас уже есть Google-аккаунт, или электронная почта. Логинимся на страничке Гугла и идем по адресу https://console.developers.google.com/cloud-resource-manager , где создаем новый проект. Даем ему произвольное название, например Beerware. Дальше переходим в другое место https://console.developers.google.com/apis/credentials. Жмем кнопочку «Создать учетные данные» и выбираем «Идентификатор клиента OAuth», и дальше отмечаем «Другие типы». Дадим название идентификатору — скажем Beerware Script.
Не спрашивайте меня, что означает разнообразие в выборе различных опций. Погружаться в это можно долго, важно лишь что наш вариант — рабочий.
Это все, что нужно было сделать в девелоперских панелях Гугла. Если вы нажмете на редактирование идентификатора, то увидите ID клиента и секрет клиента. Запомним эти названия, они понадобятся нам дальше. Чтобы не путаться с обозначениями при подстановке в другие строки, обозначим их CLIENT_ID и CLIENT_SECRET.
Authorization Flow: как проходит авторизация OAuth2
Конечной целью авторизации является получение ACCESS_TOKEN, который необходимо использовать каждый раз когда мы делаем запрос к адресной книге. Теперь нам нужно внимательно пройтись по всем этапам. Самая главная вещь, которая толком не описана в документации Google — какие этапы выполняются только один раз, а какие нужно выполнять периодически. Я буду это подчеркивать.
Каждый этап — это обращение к серверу Google по HTTPS и получение ответа. Детали запроса я распишу позже, сейчас главное — уяснить общую схему.
Получение кода авторизации
Код авторизации сразу ставит в тупик. Зачем он нужен, если уже есть CLIENT_SECRET? Разница в том, что код авторизации используется только один раз — для получения токенов. Замечу сразу, что если вы попытаетесь получить по одному и тому же коду авторизации токены еще раз, то получите ошибку. Таким образом, код авторизации действителен пока мы еще не получили токены. После их получения он сразу «протухает». Но об этом — дальше.
CLIENT_SECRET, как и CLIENT_ID — постоянные строки, скрытые в нашем приложении вдали от посторонних глаз.
Итак, посылаем на сервер CLIENT_ID, CLIENT_SECRET и получаем код авторизации. Назовем его AUTH_CODE.
Получение токенов
И снова заголовок наводит на размышления. Почему токены во множественном числе, когда вначале говорилось только об ACCESS_TOKEN? На самом деле, помимо этого токена, который используется при работе с адресной книгой, нам дают еще REFRESH_TOKEN. Его роль будет ясна ниже.
Посылаем на сервер CLIENT_ID, CLIENT_SECRET, AUTH_CODE и получаем парочку ACCESS_TOKEN, REFRESH_TOKEN.
Казалось бы, тут можно забыть о REFRESH_TOKEN, поскольку ACCESS_TOKEN у нас в руках, и дальше еще забыть и про авторизацию и спокойно пользоваться ACCESS_TOKEN во всех запросах. Я так поначалу и делал, только обнаружил что в один прекрасный момент сервер перекрыл мне доступ. В чем дело? А причина была в том, что по задумке Google время жизни ACCESS_TOKEN — всего лишь час, после чего он протухает и работать с ним не получится.
Что теперь, снова получать код авторизации и токены? Нет. Про эти два этапа «получение кода авторизации» и «получение токенов» можно полностью забыть и уже никогда к ним не возвращаться. Самое главное, что у нас есть — это REFRESH_TOKEN. Сохраните его в надежном месте, поскольку теперь кроме него нам ничего не нужно.
Замечу, что эти пройденные этапы мы выполняем лично. Наше приложение Beerware Script о них ничего не знает: ему нужны только токены.
Получение ACCESS_TOKEN по REFRESH_TOKEN
Дальше все становится просто. Когда ACCESS_TOKEN протухает, то есть через час становится не действительным, или просто не дожидаясь этого момента, мы посылаем на сервер CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN и получаем обновленное значение ACCESS_TOKEN, которое используем для запросов к серверу. Важно знать, что при этом REFRESH_TOKEN не меняется. Поэтому для приложения критично запомнить его, например в файле, при получении в самый первый раз.
Все этапы авторизации из командной строки
Для различных языков есть соответствующие библиотеки OAuth2. Мы же, для ясности изложения, все будем делать ручками из командной строки, формируя запросы с помощью утилиты curl. В конце концов, все эти библиотеки делают тоже самое — формируют url запросы к серверу Google.
Начнем с получения кода авторизации. Поскольку эта процедура выполняется только раз, не грех поработать вместо нашего приложения. Вбиваем в адресную строку броузера и наблюдаем:
1 2 3 |
https://accounts.google.com/o/oauth2/auth?сlient_id=CLIENT_ID &redirect_uri=http://localhost:8080 &scope=https://www.google.com/m8/feeds/&response_type=code |
Запросом response_type=code мы хотим увидеть код авторизации. Он будет переслан по адресу redirect_uri, а поскольку на URI=http://localhost:8080 ни у меня, ни думаю у вас — ничего нет, то естественно браузер даст ошибку. Но это неважно, поскольку искомый код появится в адресной строке браузера и мы возьмем его оттуда, копируя начиная с code= и продолжая до символа #, который присутствует в конце url. Так мы получаем AUTH_CODE.
В середине процедуры вас может пробросить на страницу авторизации, где вы должны подтвердить доступ к адресной книге со стороны проекта Beerware.
Теперь получаем токены. На запрос
1 2 3 |
curl -X POST -d "code=AUTH_CODE&client_id=CLIENT_ID &client_secret=CLIENT_SECRET&redirect_uri=URI &grant_type=authorization_code" https://accounts.google.com/o/oauth2/token |
сервер выдаст нам долгожданную парочку токенов:
1 2 3 4 5 6 |
{ "access_token" : "XXXXXXXXXXXXXXXX", "expires_in" : 3600, "refresh_token" : "XXXXXXXXXXXXXXXX", "token_type" : "Bearer" } |
Если мы попробуем получить токены еще раз, с использованным кодом авторизации, нас ожидает ошибка:
1 |
Authorization code redeemed |
Все, код авторизации протух и с ним уже сделать ничего нельзя!
На этом ручная работа закончилась и скрипт Beerware Script может начинать работу. Отдаем ему ACCESS_TOKEN и REFRESH_TOKEN и забываем про авторизацию. Все, что нужно делать скрипту когда ACCESS_TOKEN становится недействительным — это получить его новое значение запросом
1 2 3 4 |
curl -X POST -d "client_id=CLIENT_ID &client_secret=CLIENT_SECRET &refresh_token=REFRESH_TOKEN &grant_type=refresh_token" https://accounts.google.com/o/oauth2/token |
Само собой, это будет уже не утилита curl, а https запрос с соответствующей библиотеки нашего приложения Beerware Script.
После запроса мы получим новый ACCESS_TOKEN:
1 2 3 4 5 |
{ "access_token" : "XXXXXXXXXXXXXXXX", "expires_in" : 3600, "token_type" : "Bearer" } |
Поработаем с адресной книгой
Теперь самое время посмотреть, как будет выглядеть работа со списком адресов. Как обычно, на этом этапе нам ассистирует Python. Ниже — фрагменты скрипта Beerware Script, которые дают представление об общей идее работы с полученным списком.
Вначале затребуем список:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#!/usr/bin/python import json import requests access_token = 'XXXXXXXXXXXXXXXX' # область работы scope - с Google Contacts scope = 'https://www.google.com/m8/feeds' server = "https://www.google.com/m8/feeds/contacts/default/full?access_token=%s&scope=%s&alt=json" # получаем кусок адресной книги используя access_token r = requests.get(server % (access_token,scope)) # это может пригодиться для отладки, если мы захотим увидеть ответ сервера при ошибке # print(r.content) # обычный результат запроса # 200 = Ok # 403 = ошибка авторизации, самое время обновить access_token! # но этот код вы напишете сами print(r.status_code) # получили адресную книгу в виде структуры dict data = json.loads(r.text) |
В комментах видно, в каком месте нужно ловить ошибку авторизации, когда ACCESS_TOKEN устареет. Дальше нужно что-то делать с полученным списком адресов, наверное самое очевидное — это получить номера телефонов.
Делается это в цикле по всей структуре data:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# все искомое расположено ниже уровней feed, entry # цикл по контактам for r in data['feed']['entry']: # распечатаем весь контакт: просто для наглядности и отладки print(json.dumps(r, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ': '))) # вытаскиваем имя контакта name = r['title']['$t'].encode('utf-8') # если контакт пустой, все пропускаем if name == '': continue print(name) # страхуемся: а вдруг у контакта нет телефона? тогда вылетим из скрипта с ошибкой if 'gd$phoneNumber' in r: # зачем цикл? а потому что телефонных номеров может быть больше одного for t in r['gd$phoneNumber']: phone = t['$t'].encode('utf-8') print("\t%s" % phone) |
В результате после работы скрипта получим список контактов, где под каждой фамилией будет список телефонов. Тема отдельного рассмотрения — как изменять существующие контакты и заводить новые. Это решается засылкой на сервер соответствующей xml записи. Подробно процедура описана здесь. Думаю, вы и сами отлично справитесь с этой задачей )
Отличная статья- это то, что я искал, но код авторизации не появляется в адресной строке браузера.
Посмотрите на URL, там должна быть строчка «code=», после нее будет код авторизации