В процессе изучения Python стало интересно попробовать его в связке с API VK. В ВК есть телефонная книга, она показывает телефоны ваших друзей в более-менее удобном формате. Так как далеко не всегда люди охотно оставляют там полые(валидные) номера своих телефонов, мне показалась интересной идея написать скрипт, который отбирал бы только валидные номера моб.телефонов и выдавал бы их отдельной таблицей. Наша телефонная книга будет генерировать csv-файл, который затем можно будет открыть, например, в excel.
Для использования API VK на Python я нагуглил отличную, на мой взгляд, библиотеку с оригинальный названием vk.
Итак, импортируем необходимые модули:
import vk
from time import sleep
from re import sub, findall
from getpass import getpass
from csv import writer, QUOTE_ALL
Создадим класс User с необходимыми методами:
class User(object):
"""VK User"""
def __init__(self, login, password):
self.login = login
self.password = password
self.id = ''
# аторизирует юзера
def auth(self):
session = vk.AuthSession(app_id='5340228', user_login=self.login, user_password=self.password)
api = vk.API(session)
return api
# возвращает массив объектов друзей
def friends(self, api):
# возвращает в том порядке, в котором расположены в разделе Мои
user_friends = api.friends.get(user_id=self.id, order='hints')
return user_friends
# возвращает количество друзей
def friends_count(self, api):
user_friends = User.friends(self, api)
friends_count = len(user_friends)
return friends_count
# возвращает массив данных о юзере
def info(self, api):
user = api.users.get(user_id=self.id)
return user[0]
Долго не мог решить проблему, и в гугле как-то не попадалось на глаза, как взять id текущего пользователя. По счастливой случайности нашел выход — надо передать в качестве аргумента пустую строку.
Далее напишем функцию валидатор, которая будет приводить мобильные номера к общему виду. Мне, как жителю Украины, интересно выбирать только украинские моб.номера, которые должны начинаться на «0». Скрипт легко подправить под любой формат.
def norm_mob(str):
if len(str) != '':
norm_mob = sub(r'(\s+)?[+]?[-]?', '', str)
# проверяем строку на наличие в ней только необходимых символов
right_mob = findall(r'[\d]', norm_mob)
# если количество знаков в двух строках совпадает, значит это номер телефона
if (len(right_mob) == len(norm_mob)) and (len(norm_mob) >= 10):
rev_norm_mob = norm_mob[::-1]
norm_mob = rev_norm_mob[0:10]
if norm_mob[::-1][0] == '0':
return norm_mob[::-1]
else:
return False
Далее проходим по друзьям, выбираем тех, кто оставлял свои контакты, и если оставлял, то валидируем их и записываем в массив. Сервер ВК не любит большого количества запросов, поэтому заставим наш скрипт спать некоторое время между ними, пробовал разные значения, оптимальными оказались эти.
def find_correct_phone_numbers(api, friends, friends_count):
users_phones = []
for i in range(0, friends_count):
cur_user_id = int(friends[i])
cur_user = api.users.get(user_id=cur_user_id, fields='contacts')
try:
# выбираем номер мобильного телефона
cur_mob = cur_user[0]['mobile_phone']
except KeyError:
sleep(0.3)
continue
mob = norm_mob(cur_mob)
if mob:
# вставим еще одну строку в наш массив
users_phones.append({
'user_name': '{} {}'.format(cur_user[0]['first_name'], cur_user[0]['last_name']),
'user_phone': '8{}'.format(mob)
})
sleep(0.4)
return users_phones
Сохраняем полученный результат.
def saveCSV(data, path):
with open(path, 'w') as csvfile:
my_writer = writer(csvfile, delimiter=' ', quotechar='"', quoting=QUOTE_ALL)
my_writer.writerow(('Имя пользователя', 'Номер моб. телефона'))
for item in data:
try:
my_writer.writerow((item['user_name'], item['user_phone']))
except Exception:
my_writer.writerow(('(Ошибка в кодировке)', item['user_phone']))
Добавим функцию, которая считала бы затраченное время.
class Timer(object):
def __enter__(self):
self._startTime = time()
def __exit__(self, type, value, traceback):
howLong = time() - self._startTime
print("Операция заняла: {:.2f} минут".format(howLong/60))
Ну и заключительный этап, сделаем вызов написанных функций.
def main():
while True:
login = input('E-mail: ')
password = getpass('Password: ')
try:
vk_user = User(login, password)
api = vk_user.auth()
print('Авторизация выполнена успешно!')
break
except Exception:
print('Вы ввели неверные данные, пожалуйста, повторите попытку.')
friends = vk_user.friends(api)
friends_count = vk_user.friends_count(api)
print('Найдено {} друзей.'.format(friends_count))
print('Идет выборка мобильных номеров...')
with Timer() as p:
users_phones = find_correct_phone_numbers(api, friends, friends_count)
print('Выборка окончена. Сохранение...')
saveCSV(users_phones, 'vk_mob.csv')
print('Данные успешно сохранены.')
if __name__ == '__main__':
main()
На выходе получаем csv-файл, который можно открыть в excel в формате удобной таблицы.
Комментарии (17)
soon
21.03.2016 20:25+1if len(str) != '': вернет True для любых str. Кроме того, str — встроенный тип, использовать его как имя не стоит. Также внутри функции norm_mob определяется переменная с таким же названием(norm_mob), что, на мой взгляд, не очень красивое решение. Отметил бы еще, что смешивать underscore_case и camelCase тоже не самый лучший подход.
aspidov
21.03.2016 20:26Долго не мог решить проблему, и в гугле как-то не попадалось на глаза, как взять id текущего пользователя. По счастливой случайности нашел выход — надо передать в качестве аргумента пустую строку.
При использовании клиентской авторизации в вк id текущего пользователя будет прилетать вместе с access-токеном на redirect uri.
angru
21.03.2016 20:26Как-то вы с классами в процедурном стиле работаете. Состояние можно хранить внутри обьекта класса, тогда не нужно будет его каждый раз передавать в метод класса(это я про api). Также, вы, вроде, инкапсулируете использование api, а потом используете его напрямую в функции find_correct_phone_numbers.
for i in range(0, friends_count):
for curr_user_id in friends:
19as
21.03.2016 23:27+4Думал, что статья будет все таки про валидные номера, которые именно привязаны к аккаунту. а не указаны в качестве телефона связи. И не понял метод валидации номера телефона
ensom
22.03.2016 13:11Насколько я понял, валидация происходит по параметру «номер телефона/не номер телефона» по количеству знаков, начальному символу (0) и специальным символам (+, разделители), и не проверяет телефон никаким другим способом (например, доступность). Другими словами, нужна только для того, чтобы отсечь номера вида «телефона-нет», вместо цифр номера. Поправьте меня, если я не прав.
Miraage
Слово «отбираем», в заголовке статьи, лучше бы заменить на «фильтруем» или «получаем».