[Из песочницы] Отбираем валидные мобильные номера друзей VK на Python

048d054c7a374090a39e3466334f922c.jpg

В процессе изучения 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 в формате удобной таблицы.

© Habrahabr.ru