Статья Поиск в ВК фото с геометками в заданном диапазоне дат с помощью Python. Часть #02

В предыдущей статье мы получили от пользователя данные, которые нам нужны будут для запуска функции поиска фото в ВК с помощью API по геоданным в определенном диапазоне дат. Теперь же нужно написать функции, которые получают информацию и отвечают за ее обработку. Давайте этим и займемся.

02.jpg



Дисклеймер: Данная статья предоставлена для ознакомления, и не призывает ни к каким действиям.


Поиск фото по координатам

Первую функцию, которая будет получать основные данные у ВК, я назову get_photo(lat, long, start_time, end_time, int(count), int(radius)). Согласно документации API метод «photos.search», а использовать мы будем именно его, требует передачи в него определенных параметров. На самом деле их гораздо больше, чем мы будем использовать. Но в данном контексте они не нужны, а потому ограничимся некоторыми из них. А с более полным списком параметров можно ознакомится в документации.

В нашем случае нам потребуются координаты, которые мы получили в пользовательском вводе. Об этом написано в предыдущей статье. Дата начала поиска, дата окончания поиска, целое число, которое определяет количество получаемых результатов поиска. И радиус поиска, который так же является целым числом и передается в метрах. Что касается количества результатов поиска, то тут еще есть параметр offset, которым можно регулировать смещение поиска от предыдущего, так как за один этап можно получить только лишь 1000 результатов. Его я изменять не стал, оставил 0. То есть, получается, что мы сможем получить с помощью данного скрипта не более 1000 результатов. Но, думаю, что в данном случае этого достаточно. А offset, при необходимости, можно реализовать, самостоятельно добавив цикл.

Python:
def get_photo(latitude, longitude, start_time, end_time, count, radius):
    params = {'access_token': token, 'lat': latitude, 'long': longitude, 'start_time': start_time,
              'end_time': end_time, 'offset': 0, 'count': count, 'radius': radius, 'v': 5.131}
    vk_session = VkApi(token=token)
    try:
        search_result = vk_session.method('photos.search', params)
    except ApiError:
        print('\n[+] Введены неверные параметры для поиска! Повторите ввод!\n')
        return

    list_res = []

    for num, res in enumerate(search_result['items']):
        print(f'\rОбработка локации: {num}', end='')
        url = ""
        height = 0
        for size in res['sizes']:
            if size['height'] > height:
                height = size['height']
                url = size['url']
        try:
            lat = res['lat']
        except KeyError:
            lat = latitude
        try:
            long = res['long']
        except KeyError:
            long = f"{longitude[0:6]}{random.randrange(100, 1000, 50)}"

        data = {'user_id': res['owner_id'], 'date': res['date'], 'lat': lat, 'long': long, 'url': url}

        user_info = get_user_info(res['owner_id'])
        data['user_info'] = user_info
        time.sleep(0.1)
        addr = get_addr((float(lat), float(long)))
        try:
            data['address'] = addr.address
        except AttributeError:
            data['address'] = "No address"

        list_res.append(data)
    return list_res

Для начала определим словарь, в который и будем передавать полученные от пользователя значения. После чего создаем сессию ВК API, в которую передаем полученный ранее токен.

Python:
params = {'access_token': token, 'lat': latitude, 'long': longitude, 'start_time': start_time,
              'end_time': end_time, 'offset': 0, 'count': count, 'radius': radius, 'v': 5.131}
    vk_session = VkApi(token=token)

Запрашиваем данные у ВК с помощью vk_session.method, куда передаем метод для работы и параметры определенные ранее.

Python:
    try:
        search_result = vk_session.method('photos.search', params)
    except ApiError:
        print('\n[+] Введены неверные параметры для поиска! Повторите ввод!\n')
        return

Заключим все это в блок try-except, чтобы в случае ошибки мы могли ее обработать.

Двигаемся дальше. Создадим список, в который будем помещать полученные результаты. Это будет этакий список json-ов, который мы и будем разбирать при сохранении.

list_res = []

Теперь запускаем цикл по полученным от ВК результатам. Здесь, с помощью enumerate, пронумеруем итерации, чтобы выводить в терминал информацию о выполнении обработки. Создадим пустую строковую переменную url, в которой будем хранить ссылку. А также зададим стартовое значение height равным 0, с помощью которого будем сравнивать размеры полученных фото.

Python:
    for num, res in enumerate(search_result['items']):
        print(f'\rОбработка локации: {num}', end='')
        url = ""
        height = 0

Здесь дело в том, что в json, что прилетает от ВК содержаться различные размеры фото. А потому, нам надо отфильтровать результаты и забрать только самые большие фото. Это не влияет на сбор остальных параметров, так как они одинаковы у всех размеров. По сути, это словарик в словарике, только с размерами фото и ссылками на них.

Следующий этап – это запуск в основном дополнительного цикла. В нем мы будем фильтровать размеры, и получать данные.

Python:
        for size in res['sizes']:
            if size['height'] > height:
                height = size['height']
                url = size['url']
        try:
            lat = res['lat']
        except KeyError:
            lat = latitude
        try:
            long = res['long']
        except KeyError:
            long = f"{longitude[0:6]}{random.randrange(100, 1000, 50)}"

В коде выше сравниваем размеры фото. Если размер в словаре больше, присваиваем его значение переменной. А также забираем ссылку на фото, значение широты и долготы. Если широты и долготы не содержится, а бывает и такое, то присваиваем значениям стартовые позиции, которые вводил пользователь. Здесь надо пояснить, что значения, которые ввел пользователь, будут всегда одинаковы. А значит, когда вы будете добавлять эти данные на карту, у вас все эти точки сольются в одну. Следовательно, чтобы этого не произошло, нужно добавить небольшое смещение в долготу, которое я и добавляю с помощью выбора случайного значения из диапазона. После чего цикл повторяется до достижения конца словаря.

Создаем словарик, в который добавляем полученные на данном этапе значения.

Python:
data = {'user_id': res['owner_id'], 'date': res['date'], 'lat': lat, 'long': long, 'url': url}

Теперь, так как у нас есть user_id, мы можем запросить у ВК данные по пользователю с помощью другого метода. Эту функцию я опишу чуть позже. А пока получаем данные с помощью функции запроса данных о пользователе. Добавляем полученные данные в наш словарик. Здесь надо бы было делать проверку, так как иногда ответ приходит пустым. И добавляются нулевые значения. Но я этого делать не стал, для упрощения распарсивания в последующем. После чего делаем небольшую паузу. Совсем небольшую. Иногда API возвращает ошибку, если пытаешься получить данные слишком быстро.

Python:
user_info = get_user_info(res['owner_id'])
data['user_info'] = user_info
time.sleep(0.1)

На следующем этапе получаем с помощью обратного геокодирования адрес на основании переданных в функцию координат. Ее я опишу тоже немного позже. Тут уже обрабатываем ошибку, так как иногда адрес по координатам не находиться и в этом случае в словарь мы добавим значение «No address».

Python:
        addr = get_addr((float(lat), float(long)))
        try:
            data['address'] = addr.address
        except AttributeError:
            data['address'] = "No address"

        list_res.append(data)

После добавляем сформированный словарь в список и повторяем, пока не кончится полученный json. Когда будет, достигнут его конец, возвращаем словарь для дальнейшей с ним работы.


Получаем данные о пользователе разместившем фото

Для начала разместим саму функцию, а далее я дам некоторые пояснения по ней.

Python:
def get_user_info(user_ids):
    session = VkApi(token=token)
    vk = session.get_api()
    user_info = vk.users.get(user_id=user_ids, fields='bdate, city, country, photo_max_orig')
    data = {}
    try:
        data['first_name'] = user_info[0]['first_name']
    except (KeyError, IndexError):
        pass
    try:
        data['last_name'] = user_info[0]['last_name']
    except (KeyError, IndexError):
        pass
    try:
        data['bdate'] = user_info[0]['bdate']
    except (KeyError, IndexError):
        pass
    try:
        data['country'] = user_info[0]['country']['title']
    except (KeyError, IndexError):
        pass
    try:
        data['city'] = user_info[0]['city']['title']
    except (KeyError, IndexError):
        pass
    try:
        data['vk_prof'] = f'https://vk.com/id{user_ids}'
    except (KeyError, IndexError):
        pass
    try:
        data['photo'] = user_info[0]['photo_max_orig']
    except (KeyError, IndexError):
        pass

    return data

Создадим функцию get_user_info(user_ids), в которую передадим user_id, который и нужен для получения данных. Также нужен токен, который был получен нами ранее. Создадим сессию и передадим токен. После чего будем использовать немного другую функцию получения данных get_api.

Python:
session = VkApi(token=token)
vk = session.get_api()

Вызовем нужный метод получения данных users.get. Передаем user_id. Также тут нужно передать дополнительные поля, так как в упрощенном, то есть базовом варианте возвращается только имя, фамилия. Передадим параметры, которые означают по порядку дату рождения, город, страну, и ссылку на фото в максимальном качестве.

Python:
user_info = vk.users.get(user_id=user_ids, fields='bdate, city, country, photo_max_orig')

Создадим словарь, в который будем добавлять полученные данные. Не у всех пользователей заполнены необходимые поля. Некоторые поля не передаются из-за настроек приватности, а у некоторых пользователей не передаются вообще никакие поля, так как стоит запрет на работу с приложениями. Потому, заключаем каждый пункт в try-except. Чтобы о пользователе присутствовала хоть какая-то информация, формируем ссылку на его страницу, которая добавляется в любом из случаев, есть или нет данные в полученном результате. Затем передаем данные для дальнейшей обработки.


Получение адреса с помощью обратного геокодирования

В данной функции была использована библиотека geopy, из которой нам понадобиться модуль Nominatim для получения адреса и GeocoderUnavailable, чтобы обработать ошибку, если адреса по данным координатам найдено не будет.

Python:
def get_addr(location):
    try:
        geoloc = Nominatim(user_agent="GetLoc")
        locname = geoloc.reverse(location)
        return locname.address
    except GeocoderUnavailable:
        locname = 'Unknown'
        return locname

Передаем в функцию координаты и получаем на выходе адрес, который возвращаем в функцию для работы.


Отмечаем координаты на карте

Создадим функцию get_map(location, lat, long, radius). Здесь на вход передаются следующие данные: сформированный список словарей, из которого будет получена информация. Начальные координаты, которые вводил пользователь, для того, чтобы определять точку старта, с которого будет запускаться карта. И радиус поиска фото. Он понадобиться для сохранения страницы html.

Python:
def get_map(location, lat, long, radius):
    area_all = folium.Map(location=(float(lat), float(long)), zoom_start=8)
    for loc in location:
        user_id = str(loc['user_id']).replace("-", "")
        lat_long = (float(loc["lat"]), float(loc["long"]))
        try:
            date = dt.datetime.utcfromtimestamp(loc["date"]).strftime('%d.%m.%Y %H:%M:%S')
            popup = f'<a target="_blank" href="{loc["url"]}">{lat_long}<br><br>{date}<br><br>{loc["address"]}</a>'
            folium.Marker(location=lat_long, popup=popup, icon=folium.Icon(color='red')).add_to(area_all)
        except KeyError:
            popup = f'<a target="_blank" href="{loc["url"]}">{lat_long}<br><br>{loc["address"]}</a>'
            folium.Marker(location=lat_long, popup=popup, icon=folium.Icon(color='red')).add_to(area_all)

        if not os.path.isfile(os.path.join(os.getcwd(), 'users_link', user_id, f'{user_id}.html')):
            area = folium.Map(location=(float(loc['lat']), float(loc['long'])), zoom_start=10)
            for usr in location:
                if user_id == str(usr['user_id']).replace("-", ""):
                    lat_long = (float(usr["lat"]), float(usr["long"]))
                    try:
                        date = dt.datetime.utcfromtimestamp(usr["date"]).strftime('%d.%m.%Y %H:%M:%S')
                        popup = f'<a target="_blank" href="{usr["url"]}">{lat_long}<br><br>{date}<br><br>' \
                                f'{usr["address"]}</a>'
                        folium.Marker(location=lat_long, popup=popup, icon=folium.Icon(color='red')).add_to(area)
                    except KeyError:
                        popup = f'<a target="_blank" href="{usr["url"]}">{lat_long}<br><br>{usr["address"]}</a>'
                        folium.Marker(location=lat_long, popup=popup, icon=folium.Icon(color='red')).add_to(area)

            if not os.path.isdir(os.path.join(os.getcwd(), 'users_link')):
                os.mkdir(os.path.join(os.getcwd(), 'users_link'))
            if not os.path.isdir(os.path.join(os.getcwd(), 'users_link', user_id)):
                os.mkdir(os.path.join(os.getcwd(), 'users_link', user_id))
            area.save(os.path.join(os.getcwd(), 'users_link', user_id, f'{user_id}.html'))
    area_all.save(os.path.join(os.getcwd(), 'users_link', f'result_(({lat},{long})_{radius}).html'))

Честно говоря, сам не ожидал, что накатаю такую портянку. Но, тут выполняются две задачи. Наносятся координаты на персонализированные карты для каждого пользователя. А также формируется общая карта со всеми геометками. Тут надо сказать, что она, в данном случае не будет особо качественной, в силу такой особенности геометок, как одинаковость.

К примеру, пользователь сделал две фото из одного места. И да, фото будут разные, а метки то одинаковые. А значит, они будут нанесены на карту одна на другую. Как бороться с данной проблемой я пока еще не понял. Возможно, тут надо будет как-то определять, была ли нанесена уже данная метка и добавлять небольшое смещение.

Еще мне не понравилась особенность folium, что он имеет ограничение по приближению карты. Иногда две метки стоят слишком близко друг к другу. И приходится очень хорошо прицелиться мышкой, чтобы попасть куда нужно. Здесь, как вариант, нужно использовать свои собственные иконки. Но какие, я пока еще не решил, а потому, остается все по умолчанию.

Теперь к коду. Запускается цикл по полученному списку словарей. Получается стартовый user_id. Устанавливаются стартовые координаты для слоя. Обрабатывается общий слой со всеми геометками. Получаем дату, формируем сообщение которое появляется при клике на метку. В него помещаем ссылку, координаты, дату фото и адрес. Наносим все это на слой с помощью folium.Marker. Ну и обрабатываем ошибку получения значения ключа, так как даты фото иногда может не быть.

Python:
        try:
            date = dt.datetime.utcfromtimestamp(loc["date"]).strftime('%d.%m.%Y %H:%M:%S')
            popup = f'<a target="_blank" href="{loc["url"]}">{lat_long}<br><br>{date}<br><br>{loc["address"]}</a>'
            folium.Marker(location=lat_long, popup=popup, icon=folium.Icon(color='red')).add_to(area_all)
        except KeyError:
            popup = f'<a target="_blank" href="{loc["url"]}">{lat_long}<br><br>{loc["address"]}</a>'
            folium.Marker(location=lat_long, popup=popup, icon=folium.Icon(color='red')).add_to(area_all)

Дальнейшие действия по нанесению меток будут однотипными, а потому, думаю, что в пояснении не нуждаются. Далее по коду делаем проверку, есть ли у нас файл с информацией о пользователе. Для того, чтобы просто пропустить данные о нем и двигаться дальше. Если нет, запускаем другой цикл по полученному списку словарей, из которого забираем информацию только о текущем пользователе. Делаем мы это путем сравнения user_id полученного в начале и полученного из списка.

Python:
        if not os.path.isfile(os.path.join(os.getcwd(), 'users_link', user_id, f'{user_id}.html')):
            area = folium.Map(location=(float(loc['lat']), float(loc['long'])), zoom_start=10)
            for usr in location:
                if user_id == str(usr['user_id']).replace("-", ""):

Далее по окончании цикла создаются директории. Общая, если еще нет и для пользователя, в том случае, если ее еще не существует. И сохраняется сформированная карта с метками по пользователю.

Python:
            if not os.path.isdir(os.path.join(os.getcwd(), 'users_link')):
                os.mkdir(os.path.join(os.getcwd(), 'users_link'))
            if not os.path.isdir(os.path.join(os.getcwd(), 'users_link', user_id)):
                os.mkdir(os.path.join(os.getcwd(), 'users_link', user_id))
            area.save(os.path.join(os.getcwd(), 'users_link', user_id, f'{user_id}.html'))

После чего все повторяется и в конце записывается карта с общими метками.

Python:
area_all.save(os.path.join(os.getcwd(), 'users_link', f'result_(({lat},{long})_{radius}).html'))

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

Python:
# pip install vk-api
# pip install folium
# pip install geopy
# pip install python-docx
# pip install pyfiglet

import datetime as dt
import os.path
import random
import requests
import time
from datetime import timedelta

import docx
import folium
import pyfiglet
from docx.shared import Pt
from geopy.exc import GeocoderUnavailable
from geopy.geocoders import Nominatim
from vk_api import VkApi
from vk_api.exceptions import ApiError

from set import token


# проставляем метки на персонализированых картах
# и все найденные метки на общей
def get_map(location, lat, long, radius):
    area_all = folium.Map(location=(float(lat), float(long)), zoom_start=8)
    for loc in location:
        user_id = str(loc['user_id']).replace("-", "")
        lat_long = (float(loc["lat"]), float(loc["long"]))
        try:
            date = dt.datetime.utcfromtimestamp(loc["date"]).strftime('%d.%m.%Y %H:%M:%S')
            popup = f'<a target="_blank" href="{loc["url"]}">{lat_long}<br><br>{date}<br><br>{loc["address"]}</a>'
            folium.Marker(location=lat_long, popup=popup, icon=folium.Icon(color='red')).add_to(area_all)
        except KeyError:
            popup = f'<a target="_blank" href="{loc["url"]}">{lat_long}<br><br>{loc["address"]}</a>'
            folium.Marker(location=lat_long, popup=popup, icon=folium.Icon(color='red')).add_to(area_all)

        if not os.path.isfile(os.path.join(os.getcwd(), 'users_link', user_id, f'{user_id}.html')):
            area = folium.Map(location=(float(loc['lat']), float(loc['long'])), zoom_start=10)
            for usr in location:
                if user_id == str(usr['user_id']).replace("-", ""):
                    lat_long = (float(usr["lat"]), float(usr["long"]))
                    try:
                        date = dt.datetime.utcfromtimestamp(usr["date"]).strftime('%d.%m.%Y %H:%M:%S')
                        popup = f'<a target="_blank" href="{usr["url"]}">{lat_long}<br><br>{date}<br><br>' \
                                f'{usr["address"]}</a>'
                        folium.Marker(location=lat_long, popup=popup, icon=folium.Icon(color='red')).add_to(area)
                    except KeyError:
                        popup = f'<a target="_blank" href="{usr["url"]}">{lat_long}<br><br>{usr["address"]}</a>'
                        folium.Marker(location=lat_long, popup=popup, icon=folium.Icon(color='red')).add_to(area)

            if not os.path.isdir(os.path.join(os.getcwd(), 'users_link')):
                os.mkdir(os.path.join(os.getcwd(), 'users_link'))
            if not os.path.isdir(os.path.join(os.getcwd(), 'users_link', user_id)):
                os.mkdir(os.path.join(os.getcwd(), 'users_link', user_id))
            area.save(os.path.join(os.getcwd(), 'users_link', user_id, f'{user_id}.html'))
    area_all.save(os.path.join(os.getcwd(), 'users_link', f'result_(({lat},{long})_{radius}).html'))


# получаем адрес по координатам
def get_addr(location):
    try:
        geoloc = Nominatim(user_agent="GetLoc")
        locname = geoloc.reverse(location)
        return locname.address
    except GeocoderUnavailable:
        locname = 'Unknown'
        return locname


# получение информации о пользователе и добавление в список для последующей обработки
def get_user_info(user_ids):
    session = VkApi(token=token)
    vk = session.get_api()
    user_info = vk.users.get(user_id=user_ids, fields='bdate, city, country, photo_max_orig')
    data = {}
    try:
        data['first_name'] = user_info[0]['first_name']
    except (KeyError, IndexError):
        pass
    try:
        data['last_name'] = user_info[0]['last_name']
    except (KeyError, IndexError):
        pass
    try:
        data['bdate'] = user_info[0]['bdate']
    except (KeyError, IndexError):
        pass
    try:
        data['country'] = user_info[0]['country']['title']
    except (KeyError, IndexError):
        pass
    try:
        data['city'] = user_info[0]['city']['title']
    except (KeyError, IndexError):
        pass
    try:
        data['vk_prof'] = f'https://vk.com/id{user_ids}'
    except (KeyError, IndexError):
        pass
    try:
        data['photo'] = user_info[0]['photo_max_orig']
    except (KeyError, IndexError):
        pass

    return data


# поиск информации по запрошенным координатам, радиусу и времени
# передача в запросившую функцию для обработки
def get_photo(latitude, longitude, start_time, end_time, count, radius):
    params = {'access_token': token, 'lat': latitude, 'long': longitude, 'start_time': start_time,
              'end_time': end_time, 'offset': 0, 'count': count, 'radius': radius, 'v': 5.131}
    vk_session = VkApi(token=token)
    try:
        search_result = vk_session.method('photos.search', params)
    except ApiError:
        print('\n[+] Введены неверные параметры для поиска! Повторите ввод!\n')
        return

    list_res = []

    for num, res in enumerate(search_result['items']):
        print(f'\rОбработка локации: {num}', end='')
        url = ""
        height = 0
        for size in res['sizes']:
            if size['height'] > height:
                height = size['height']
                url = size['url']
        try:
            lat = res['lat']
        except KeyError:
            lat = latitude
        try:
            long = res['long']
        except KeyError:
            long = f"{longitude[0:6]}{random.randrange(100, 1000, 50)}"

        data = {'user_id': res['owner_id'], 'date': res['date'], 'lat': lat, 'long': long, 'url': url}

        user_info = get_user_info(res['owner_id'])
        data['user_info'] = user_info
        time.sleep(0.1)
        addr = get_addr((float(lat), float(long)))
        try:
            data['address'] = addr.address
        except AttributeError:
            data['address'] = "No address"

        list_res.append(data)
    return list_res


# запрос координат и обработка
def lat_long_input():
    coord = input('> Введите координаты для поиска:\n> (пример: 55.754255, 37.620251)\n~#: ')

    while True:
        try:
            lat = coord.split(",")[0].strip()
            long = coord.split(",")[1].strip()
            float(lat)
            float(long)
            return lat, long
        except (IndexError, ValueError):
            print('[-] Неверный ввод.')
            coord = input('> Введите координаты для поиска:\n> (пример: 55.754255, 37.620251)\n~#: ')


# запрос и обработка даты для поиска
def start_end_time_input():
    start_end = input('> Введите дату или диапазон дат для поиска:\n> (пример: 05.02.2020-07.02.2020)\n!# ')
    while True:
        try:
            start_time = start_end.split("-")[0].strip()
            end_time = start_end.split("-")[1].strip()
        except IndexError:
            start_time = start_end
            end_time = start_end

        try:
            start = f'{start_time} 00:01'
            start_t = dt.datetime.strptime(start, "%d.%m.%Y %H:%M")
            timezone_s = dt.datetime.strptime(start, "%d.%m.%Y %H:%M").astimezone()
            offset_s = int(str(timezone_s).split("+")[-1].split(":")[0])
            start_d = start_t + timedelta(hours=offset_s)
            start_unx_s = start_d.timestamp()

            end = f'{end_time} 23:29'
            end_t = dt.datetime.strptime(end, "%d.%m.%Y %H:%M")
            timezone_e = dt.datetime.strptime(end, "%d.%m.%Y %H:%M").astimezone()
            offset_e = int(str(timezone_e).split("+")[-1].split(":")[0])
            end_d = end_t + timedelta(hours=offset_e)
            end_unx_s = end_d.timestamp()
            return int(start_unx_s), int(end_unx_s)

        except ValueError:
            print('[-] Неверный ввод.')
            start_end = input('> Введите дату или диапазон дат для поиска:\n> (пример: 05.02.2020-07.02.2020)\n!# ')


# запуск функций получения данных
# запрос кол-ва результатов и радиуса поиска
# запуск функций получения фото по координатам
# запуск функции добавления меток на карту
# запуск функции сохранения данных
def main():
    print(pyfiglet.figlet_format("VK Search geodata", font="digital"))
    lat_long = lat_long_input()
    lat = lat_long[0]
    long = lat_long[1]
    start_end_time = start_end_time_input()
    start_time = int(start_end_time[0])
    end_time = int(start_end_time[1])

    count = input('> Введите количество результатов поиска:\n(по умолчанию: 50)\n~# ') or "50"
    while not count.isdigit():
        count = input('> Введите количество результатов поиска:\n(по умолчанию: 50)\n~# ') or "50"

    radius = input('> Введите радиус поиска в метрах:\n(пример: 5000 | 1 км. = 1000 м.)\n'
                   '(по умолчанию: 1000)\n~# ') or "1000"
    while not radius.isdigit():
        radius = int(input('> Введите радиус поиска в метрах:\n(пример: 5000 | 1 км. = 1000 м.)\n'
                           '(по умолчанию: 1000)\n~# ') or "1000")

    start_pril = time.monotonic()
    list_res = get_photo(lat, long, start_time, end_time, int(count), int(radius))
    get_map(list_res, lat, long, radius)
    save_data(list_res)

    print(f'\nВремя поиска {count} результатов в радиусе {radius} м. = {time.monotonic() - start_pril}')


if __name__ == "__main__":
    main()

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

Спасибо за внимание. Надеюсь, что данная информация будет вам полезна
 
Последнее редактирование:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!