1 заметка с тегом

recommendation

Где поесть? Куда сходить? Ищем ответ на вопрос с помощью пары рекомендаций и скрипта Python

Время чтения текста – 12 минут

Поскольку наш блог придерживается технологий, аналитики и IT-тематики, то обсуждение политики мы здесь, естественно опустим. Однако, сложно не заметить, что многие сейчас целенаправленно или волей случая оказываются в незнакомых городах и странах. Или планируют переезд, или просто путешествуют. В любом случае, где бы вы ни оказались, всегда хочется выстроить быт, сходить на завтрак или выпить вкусный кофе. Если вам интересно, чем в этих вопросах может помочь наш скрипт и проверить его в действии — продолжайте читать.

Что мы придумали?

Итак, предположим, что вы оказались в незнакомом городе. У вас есть несколько рекомендаций от друзей или просто несколько проверенных мест, где вам вкусно и красиво.
Наш алгоритм может быстро увеличить этот список в несколько раз, дополнив его 5-10 рекомендациями того же качества или уровня. Звучит здорово, да?

Как мы это реализовали?

Мы все еще не умеем колдовать, поэтому решили прибегнуть к более простому способу — написать Python-скрипт для решения этой задачи.
Начинаем, как всегда, с подготовки. Самая важная шестеренка в нашем скрипте — Instagram API (деятельность социальной сети признана экстремистской и запрещена в Российской Федерации).

from instagrapi import Client
import time
import pandas as pd

Затем, подключаемся к API, чтобы приступить к обработке данных.

cl = Client()
cl.login("username", "password")

Наша задача реализуется двумя небольшими скриптами. Первый собирает и обрабатывает геометки из Instagram (деятельность этой социальной сети признана экстремистской и запрещена в Российской Федерации), а также находит людей, которые отмечали эти геометки на фотографиях. Мы знаем, что эти места наверняка нравятся не только нам и предполагаем, что мы нашли тех людей, которые разделяют наши вкусы и ценности. Поэтому, мы собираем все недавние геометки в их профиле и так получаем список рекомендаций. Затем, с помощью второго скрипта мы узнаем точные адреса этих мест в Яндекс.Картах, чтобы определиться с выбором.

Сбор и обработка данных

Начинаем работу. Для того чтобы получить список рекомендаций нам нужны три подходящих примера, например кофейни Санкт-Петербурга: Смена, ТЧК, Civil. Наш скрипт принимает на вход идентификаторы геоточек заведений.
Как их получить?

  1. Переходим по ссылке в профиль заведения, например:
    https://www.instagram.com/smenacafe/
  2. Анализируем геометки в постах (считаем, что официальный профиль содержит правильные геометки)
  3. Находим ссылку на геометку, например:
    https://www.instagram.com/explore/locations/727911037416015/smenacafe/
  4. Цифры в ссылке и есть идентификатор геоточки
    727911037416015
    После того как мы нашли геометки первичных рекомендаций, нам нужно найти тех пользователей, кто ходит в это заведение (= отмечает его на своих фотографиях). Для этого мы берем последние 150 отметок заведения.
    Конечно, это должны быть реальные пользователи, а не бизнес-аккаунты, потому что там могут присутствовать рекламные интеграции, а мы в них не заинтересованы.
pk_place_ids = [541835746291313, 2103750586526340, 100059475]
 
print('Getting users started')
 
# получаем пользователей, которые отметили заведение на фото
users = []
for i in pk_place_ids:
    # получаем 150 последних постов с выбранной геометкой
    medias = cl.location_medias_recent(i, amount=150)
    
    for m in medias:
        user_id = m.dict()['user']['pk']
        if not user_id in users:
            users.append(user_id)
    
count_users = len(users)
 
print(f'Getting {count_users} users finished')
 
print('Getting not business users started')
 
# отбираем тех пользователей, чьи аккаунты не являются комерческими
users_not_business = []
for u in users:
    try:        
        u_info = cl.user_info(u).dict()
        
        if not u_info['is_business']:
            users_not_business.append(u)
    except:
        next
 
count_nb_users = len(users_not_business)
 
print(f'Getting {count_nb_users} not business users finished')
 
print('Getting location started')

Мы нашли людей, которые отмечали эти места у себя в профиле. Теперь мы собираем все места, которые отмечены в профилях этих людей и выводим эти места списком, сортируя по числу упоминаний.

# получаем места, которые посетил пользователь
locations = {}
 
end_cursor = None
for u in users_not_business:
    # скрипт работает довольно медленно, поэтому анализируем только 100 последних постов пользователя
    # посты получаем порциями 20 раз по 5 с сохранением курсора
    for page in range(20):
        u_medias, end_cursor = cl.user_medias_paginated(u, 5, end_cursor=end_cursor)
        for m in u_medias:
            # обернул в обработку исключений, т.к. иногда парсер падает
            try:
                # задержка для снижения чатсоты запросов в инстаграм
                time.sleep(1)
                # по идентификатору поста получаем данные поста (важно, что есть имя места, но нет его координат)
                info = cl.media_info(m.dict()['pk']).dict()
                if 'location' in info:               
                    loc_key = info['location']['pk']
                    
                    # вывод имени отмеченного места (для лога)
                    #print(info['location']['name'])
                    
                    # если место встретилось первый раз, то узнаем его координаты
                    if loc_key not in locations:
                        
                        # для того, чтобы узнать координаты, берем последний пост с такой геометкой
                        loc_data = cl.location_medias_recent(loc_key, amount=1)[0].dict()
                        
                        lng=''
                        lat=''
                        
                        if 'location' in loc_data:
                            lng=loc_data['location']['lng']
                            lat=loc_data['location']['lat']
                        
                        locations[info['location']['pk']] = [info['location']['name'],1,lng,lat] 
                    else:
                        locations[info['location']['pk']][1] = locations[info['location']['pk']][1] + 1
                    
                    # сохраняем результат в csv файл
                    ids = [i for i in locations]
                    names = [locations[i][0] for i in locations]
                    vizits = [locations[i][1] for i in locations]
                    lngs = [locations[i][2] for i in locations]
                    lats = [locations[i][3] for i in locations]
 
                    df = pd.DataFrame(
                        {'id': ids,
                        'name': names,
                        'vizit': vizits,     
                        'lng': lngs,
                        'lat': lats     
                        })
 
                    df.sort_values('vizit', ascending=False).to_csv('places.csv', index=False)                  
                    
            except:
                next
                
count_locations = len(locations)
 
print(f'Getting {count_locations} location finished')

Второй скрипт должен находить координаты новых рекомендаций в Яндекс.Картах.

# получим категории из справочника организаций сервиса Яндекс.Карты
import requests
 
# ключ API можно найти в кабинете разработчика
api_key = "---"
 
addrs = []
urls = []
cat1s = []
cat2s = []
cat3s = []
 
df = pd.read_csv("places.csv")
 
for i, row in df.iterrows():
    
    time.sleep(1)
    
    lng = row['lng']
    lat = row['lat']
    name = row['name']
    print(name)
        
    req = f"https://search-maps.yandex.ru/v1/?text={name}&results=1&type=biz&lang=ru_RU&ll={lng},{lat}&spn=0.01,0.01&apikey={api_key}"
 
    response = requests.get(req)
 
    addr = ''
    url = ''
    cat1 = ''
    cat2 = ''
    cat3 = ''
 
    if response.status_code == 200:
        # обернули в обработку исключений, т.к. иногда падает
        try:
            company_data = response.json()['features'][0]['properties']['CompanyMetaData']
                        
            addr = company_data['address']
            url = company_data['url']
            
            count_categories = len(company_data['Categories'])
            
            # у организации может быть до 3 категорий, сохраняем их все
            if count_categories > 0:
                if count_categories == 1:
                    cat1 = company_data['Categories'][0]['name']
                elif count_categories == 2:
                    cat1 = company_data['Categories'][0]['name']
                    cat2 = company_data['Categories'][1]['name']
                elif count_categories == 3:
                    cat1 = company_data['Categories'][0]['name']
                    cat2 = company_data['Categories'][1]['name']
                    cat3 = company_data['Categories'][2]['name']             
            
        except:
            pass
        
    addrs.append(addr)
    urls.append(url)
    cat1s.append(cat1)
    cat2s.append(cat2)
    cat3s.append(cat3)
    
df['address'] = addrs
df['url'] = urls
df['cat1'] = cat1s
df['cat2'] = cat2s
df['cat3'] = cat3s
df.to_csv('places_24.csv', index=False)

Результаты

Выпить кофе в Петербурге
Мы решили проверить, как работает скрипт на примере трех наших любимых кофеен в Петербурге. Получилось следующее:

Поужинать в Петербурге
Также мы протестировали наш скрипт, задав три рекомендации классных ресторанов Питера: Chang, Tiger Lilly, Jack and Chan.

Ограничения скрипта

  1. Скрипт сбора данных работает недостаточно быстро (примерно 100 геоточек в час).
  2. У геоточек может быть несколько дублей. Бывают геоточки с одинаковыми названиями, но отличающимися по координатам. Если честно, то по сути в геоточках просто хаос. Поэтому остается много ручной работы на этапе обработки и получения результатов.
  3. Яндекс отдает данные хорошо: база организаций очень большая, но для большинства объектов заполнена не одна, а целых три категории. У кафе может быть первая категория бар, затем кафе, затем ресторан. В общем, приходится опять же проверять многое вручную.
  4. Сделать единую процедуру пока не очень получается.
    Пока со скриптом можно работать в два этапа:
    — Первый этап — получение данных из инстаграма (название, частота посещений, координаты) и затем полуавтоматическая чистка от ненужных объектов + объединение дублей (это важно, так как бесплатная квота Яндекс.Карт — 500 запросов в день),
    — Второй этап — получение из Яндекс.Карт категорий, адреса, сайта и затем ручная сверка категорий и выбор наиболее точной категории из трех.
 Нет комментариев    294   2022   python   recommendation