Перейти к концу метаданных
Переход к началу метаданных

Вы просматриваете старую версию данной страницы. Смотрите текущую версию.

Сравнить с текущим просмотр истории страницы

« Предыдущий Версия 11 Следующий »

Ноутбуки, используемые в примере:


Тип статьи

Инструкция

Компетенции

JupyterLab, Python, Petl, Pandas, JSON

Необходимые права

Доступ к JupyterLab

Версии компонентов

Jupyter core - 4.7.1, Petl - 1.7.2 , pandas - 1.2.3, numpy - 1.19.5, appstoreconnect - 0.9.0, Sqlalchemy - 1.3.23, app-store-scraper - 0.3.5

Статус

БЕТА

Сложность

ЛЕГКО

Полезные ссылки

Petl, Pandas, appstoreconnect, numpy, app-store-scraper, Apple Store Connect API

Дополнительные сведения

ОС Ubuntu 18.04

В данной статье рассматривается пример получения данных из магазина приложений App Store с использованием Apple Store Connect API и app-store-scraper (для выгрузки отзывов о приложении). Для удобства работы с API будет использована Python библиотека appstoreconnect 0.9.0.

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

Функционал обеих библиотек схож, и вы можете использовать ту, что вам больше нравится.

Целью этого задания является получение данных по API для выгрузки их в платформу и построения дашборда.
Пример не является шаблонным: в каждом случае задачи разнятся, и работать с данными придется по-другому.

Вот особенности данного кейса:

  • Количество получаемых данных небольшое - порядка 140 строк, но для демонстрации правильного подхода все выгруженные и обработанные данные сохраняются в Excel файл и базу данных PostgreSQL. Для загрузки данных в платформу вы можете использовать любой из вариантов. Когда получаемых данных много - правильнее всего использовать запись в базу данных.

  • Будут использоваться два ноутбука: первый для данных, полученных с помощью App Store Connect API, второй для отзывов на странице приложения в магазине. Данный подход был выбран, поскольку данные для отзывов берутся другим способом и находятся в открытом доступе. Отсюда загрузка в платформу будет осуществляться с помощью двух разных таблиц (их объединение лишено смысла для нашего примера).

Здесь не рассматривается:

  • Данные будут забираться вручную. Для регулярной автоматический выгрузки необходимо настроить планировщик Chronicle.

  • Для дальнейшей загрузки в платформу можно воспользоваться стандартными средствами в платформе: “создание загрузчика” и “планы загрузки”. Всё описано в разделе документации (ссылка для версии 2.20):
    Загрузка данных и формирование структуры в аналитической базе данных ViQube

  • Подробности работы подключенных Python библиотек. Это необходимо изучать отдельно.

Сначала необходимо внимательно ознакомиться с описанием работы библиотеки Python для App Store Connect Api: appstoreconnect 0.9.0, а также с API App Store Connect.

Для работы нам понадобятся такие библиотеки, как: Appstoreconnect, Petl, Pandas, Numpy, Sqlalchemy, Requests.
Если они у вас не установлены, то вы можете воспользоваться статьей по установке библиотек:

Установка Python библиотек в ViXtract

Теперь можно приступать к работе в JupyterLab.

В первом ноутбуке мы получим данные от App Store Connect API и выгрузим их в Excel файл и базу данных PostgreSQL.


  1. Создаем новый ноутбук для информации о приложении, например, с именем appstoreconnect_example.

  2. Первым блоком подключаем все необходимые нам для работы Python библиотеки:

    import petl as etl # для загрузки и обработки данных
    import pandas as pd # для удобной работы с датой и выгрузки таблицы в postgresql
    
    import datetime as dt # для рассчёта даты
    
    from appstoreconnect import Api # для подключения к App Store Connect API
    import sqlalchemy # для создания подключения к базе данных
    
    import requests # для отправки запроса по API ViQube admin
    import json # для чтения файла json 

  3. Вторым блоком подключаемся к API App Store с помощью Python библиотеки Appstoreconnect. Данные для подключения можно получить, следуя документации Creating API Keys for App Store Connect API:

    api = Api(key_id, path_to_key_file, issuer_id) # инициализация данных для api

  4. Указываем период дат, за который будут браться отчёты. Будем использовать период с 01.01.2021 по текущую дату. Для этого обратимся к библиотеке Pandas и вызовем ее метод date_range(). В этот метод мы передаем два параметра: start (начальная дата периода), end (конечная дата периода). Их мы определяем с помощью Python библиотеки datetime.

    dateRange = pd.date_range(start = dt.datetime.today().strftime('%Y-01-01'), 
                              end = dt.datetime.today()) # установка диапазона дат


    На выходе мы получим вот такой объект:


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

  5. Создадим фиксированный словарь с кодами стран и их расшифровкой, они понадобятся для дальнейшей работы с таблицей. Если вы хотите получать коды и названия стран динамически (чтобы он всегда был актуален), то можете воспользоваться API REST Countries или другим доступным в Интернете методом.

    # коды стран
    codes = {
        'AD': 'Andorra',
        'AE': 'United Arab Emirates',
        ...
        'ZW': 'Zimbabwe',
        'ZZ': 'Unknown or unspecified country',
    }

  6. Отчёт формируется путём запроса информации за каждый день. На одну дату - один отчёт. Поэтому мы создадим список, в котором будем хранить первым элементом - названия столбцов, а последующими - информацию по датам.

    # получение отчётов
    sales=list()
    isFirstRow = True
    for reportDate in dateRange:
        try:
            resp = api.download_sales_and_trends_reports(filters={
                'vendorNumber': '{vendorNumber}', 
                'frequency': 'DAILY', 
                'reportDate': reportDate.strftime('%Y-%m-%d')
            }).partition('\n')

    Где {vendorNumber} - это ваш идентификационный номер, который вы можете узнать в своём аккаунте App Store Connect.

    При получении отчёта в переменной resp хранятся названия столбцов вместе со значениями столбцов:


    Для удобной работы с этими данными мы применяем к переменной resp строковый метод partition(). В качестве параметра мы передаем ему символ ‘\n’. В итоге получается кортеж, нулевой элемент которого – это название столбцов, первый - наш разделитель ‘\n’, а второй - собранные данные по дате:


    Поскольку всё это происходит в цикле, то данные правила будут применимы к каждому полученному отчёту.
    Так как мы хотим создать список, первым элементом которого будут названия столбцов, а другими - значения этих столбцов, то для начала нам нужна проверка для первой строки.

            if isFirstRow:
                sales.append(resp[0]) # resp[0] хранит в себе только названия столбцов
                isFirstRow = False
            sales.extend(resp[2].split('\n')) # resp[2] хранит в себе собранные данные по дате
            print(reportDate)
        except:
            continue


    После добавления названий столбцов, мы ставим переменную isFirstRow в положение False, чтобы названия столбцов не повторялись. Затем, чтобы разделить между собой значения столбцов, мы используем метод split() и передаем ему в качестве параметра символ ‘\n’. И, используя метод extend(), добавляем значения в список sales. По итогу, переменная sales содержит в себе названия столбцов (в качестве нулевого элемента) и значения столбцов (в качестве последующих элементов):

  7. Нужно привести данные в тот вид, с которым может работать библиотека Petl. Для этого мы разделим каждый элемент нашего списка sales по символу ‘\t’ с помощью уже известного метода split(). Поместим эти значения в переменную splitted:

    splitted = [x.split('\t') for x in sales if x]


    Теперь переменная splitted хранит в себе списки без лишних символов:


    Нулевой элемент этого списка – названия столбцов, а все остальные элементы – значения столбцов.

  8. Теперь библиотека Petl с лёгкостью сформирует из этого списка таблицу. Для более информативного отображения мы добавим в таблицу столбец Country, в который поместим расшифровку кодов стран из столбца Country Code. Воспользуемся методом addfield() библиотеки Petl. Этот метод в качестве параметров принимает переменную, из которой строится таблица (нашем случае это переменная splitted), название нового столбца, и значение, которым будет заполнен столбец. В качестве этого значения можно передать анонимную функцию (лямбда выражение). У нашей функции в качестве переменной x - код страны. Мы будем использовать словарный метод get(), передадим ему переменную x. Функция будет искать в нашем словаре codes ключи из столбца Country Code и возвращать значения по этим ключам. Эти значения мы и запишем в новый столбец Country.

    tbl = etl.addfield(splitted, 'Country', lambda x: codes.get(x['Country Code'])) # добавление столбец со странами


    Так будет выглядеть сформированная таблица:

  9. Как видно, она не идеальна и хранит в себе столбцы с пустыми значениями, а так же те столбцы, которые не затрагиваются в данном примере. Воспользуемся методом cut(). В качестве параметров передаем ему нашу таблицу, и далее, через запятую в апострофах, мы передаем названия тех столбцов, которые хотим оставить в таблице:

    tbl = etl.cut(tbl, 'Title', 'Version', 'Units', 'Begin Date', 'Device', 'Country') # отбор нужных столбцов


    На выходе получится такая таблица:

  10. Мы хотим отслеживать последнюю дату получения актуальных данных. Данные считаются актуальными, если мы не получили ошибок: при обращении к api; при вызове плана загрузки (опционально).

  11. Чтобы убедиться в том, что мы не получили ошибок от api и у нас имеются нужные данные, мы проверим длину переменной sales, т.к. в случае ошибки, в нее ничего не запишется. Результатом проверки будет значение True или False.

    is_api_ok = len(sales) > 0

  12. Перед тем, как проверить статус плана загрузки необходимо получить токен аутентификации.

    url = "http://{domain}/idsrv/connect/token"
     
    payload = "grant_type=password&scope=openid+profile+email+roles+viqube_api+viqubeadmin_api+core_logic_facade+dashboards_export_service+script_service+migration_service_api+data_collection&response_type=id_token+token&username={UserName}&password={Password}"
    headers = {
        'content-type': "application/x-www-form-urlencoded",
        'authorization': "Basic cHVibGljX3JvX2NsaWVudDpAOVkjbmckXXU+SF4zajY="
        }
     
    token_response = requests.request("POST", url, data=payload, headers=headers).json()

    Где {domain} – домен или IP адрес сервера платформы, {UserName} – имя пользователя, от которого будет осуществляться работа по API, {Password} – пароль пользователя

  13. Возьмем токен из полученного ответа и запишем в переменную access_token:

    access_token = token_response['access_token']

  14. Далее мы обратимся к API ViQube Admin и запросим статус плана загрузки.

    url = 'http://{domain}/vqadmin//api/databases/{databaseId}/loadplans/{loadPlanId}/status'
    headers = {
        'X-API-VERSION': "1.0",
        'authorization': f"Bearer {access_token}"
        } 
    loadplan_response = requests.request("GET", url, headers=headers).json()

    Где {domain} – домен или IP адрес сервера платформы; {databaseId} - id базы данных ViQube; {loadPlanId} - id статус нужного плана загрузки; {access_token} - токен аутентификации, полученный выше.

    Более подробно можете ознакомиться с документацией здесь.

  15. Чтобы узнать, удачно ли сработал план загрузки, мы проверим, будет ли в ответе какое-то значение у параметра Error. Если параметр отсутствует или у него нет значения - значит загрузка выполнена успешна. Результат проверки поместим в переменную is_plan_ok.

    is_plan_ok = not loadplan_response['error']

  16. Если API и план загрузки отработали верно, то запишем в переменную last_update_day сегодняшнюю дату. Также поместим значение даты в JSON файл, для дальнейшей синхронизации с датой получения отзывов

    if is_plan_ok and is_api_ok:
        last_update_day = dt.datetime.today().strftime('%Y-%m-%d')
        with open('data.json', 'w') as f:
            json.dump({'LastUpdateDay': last_update_day}, f)
    else:
        last_update_day = None

  17. Далее мы можем считать, что если в переменной last_update_day содержится значение с датой, то все запросы прошли без ошибок.

  18. Теперь можно экспортировать нашу таблицу в Excel файл для дальнейшей загрузки на платформу. Для этого воспользуемся методом toxlsx() библиотеки Petl. В качестве параметров передаем методу название Excel файла, флаг write_header=True указывает, что нужно записывать заголовки в таблицу, флаг mode='replace' указывает, что файл будет перезаписываться при повторном запуске кода.

    if last_update_day:
        tbl.toxlsx('appstoreconnect.xlsx', write_header=True, mode='overwrite') # запись в excel


    Мы получили Excel таблицу, которую можно загружать на платформу:

  19. Помимо экспорта а Excel продемонстрируем подход с сохранением данных в PostgreSQL. Установим подключение к базе данных с помощью метода create_engine() библиотеки Sqlachemy.

    engine = sqlalchemy.create_engine('postgresql://{user}:{user_password}@{url}:{port}/{database_name}') # подключение к базе данных


    Где {user} - имя пользователя базы данных, {user_password} - пароль, {url} - адрес базы данных, {port} - порт, {database_name} - название базы данных, в которой будет создана таблица.

  20. Далее создадим объект DataFrame библиотеки Pandas из нашей таблицы Petl. Воспользуемся методом DataFrame() и укажем ему, что названия колонок - это tbl[0], а значения - это срез от первого элемента таблицы Petl до последнего tbl[1:].

    if last_update_day:
        df = pd.DataFrame(columns=tbl[0], data=tbl[1:] # создание DataFrame из petl-таблицы


    Получим таблицу Pandas:

  21. Теперь экспортируем таблицу в базу данных PostgreSQL с помощью метода to_sql() библиотеки Pandas. В параметрах передаем название таблицы, переменную подключения engine, которую определили выше, флаги index=False и if_exists='replace, и словарь с типами данных для столбцов. Флаг index=False говорит, что не нужно создавать столбец с индексами. Флаг if_exists='replace' указывает, что таблицу нужно перезаписать, если она уже существует. В словаре dtype мы указываем название столбца в качестве ключа, а в качестве значения указывается тип данных библиотеки Sqlalchemy и его длина. Типы данных можно посмотреть тут.

    Например, для столбца ‘Title’ мы укажем тип данных VARCHAR с длинной 25. Указывается такая длина, т.к. в нашем примере длина названия приложения не будет больше 25-ти символов.

    # экспорт в базу данных
    if last_update_day:
        df.to_sql('mobileappdownload', engine, index=False, if_exists='replace', dtype={
            'Title': sqlalchemy.VARCHAR(25),
            'Version': sqlalchemy.VARCHAR(5),
            'Units': sqlalchemy.Integer(),
            'Begin Date': sqlalchemy.Date(),
            'Device' : sqlalchemy.VARCHAR(10),
            'Country': sqlalchemy.VARCHAR(30),
        })

Теперь можно перейти к работе со вторым ноутбуком. В нем мы будем получать данные об отзывах с помощью Python библиотеки app-store-scraper.

  1. Создаем новый ноутбук для отзывов, например, с именем appstore_reviews. Импортируем библиотеки:

    import petl as etl # для загрузки и обработки данных
    
    from app_store_scraper import AppStore # для выгрузки отзывов о приложении
    import pandas as pd # для удобной работы с датой и выгрузки таблицы в postgresql
    
    import sqlalchemy # для создания таблицы в базе данных
    
    import json # для чтения JSON файла
    import datetime as dt # для установления даты


    Чтобы избежать возможных ошибок при получении отзывов, воспользуемся конструкцией try - except.
    В блоке try создадим в переменную last_reviews - экземпляр класса AppStore, которому укажем код нашей страны country="ru" и название приложения app_name="visiology mobile", по которому хотим собрать отзывы. Затем обратимся к методу review(), который будет парсить страницу с отзывами. Укажем нужное количество отзывов в параметре how_many (в нашем случае 10). Поместим в переменную is_review все получившиеся отзывы. Это позволит нам проверить наличие ошибок при запросе. Также укажем сегодняшнюю дату, в случае правильной работы запроса.
    В блоке except укажем, что переменные last_update_day=None и is_review = False для того, чтобы избежать ошибок, если отзывы не загрузились.

    # получение отзывов
    try:
        last_reviews = AppStore(country="ru", app_name="visiology mobile")
        last_reviews.review(how_many=10)
        is_review = last_reviews.reviews
        last_update_day = dt.datetime.today().strftime('%Y-%m-%d')
    except:
        last_update_day = None
        is_review = False


    Чтобы посмотреть, как выглядят полученные отзывы, нужно обратиться к полю reviews класса Base, от которого унаследован класс AppStore. Это поле хранит в себе список с отзывами.
    Если вызвать команду:

    last_reviews.reviews


    То получится такой результат:


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

  2. Если в ходе получения отзывов не возникло ошибок, сформируем Petl таблицу, воспользовавшись методом fromdicts() библиотеки Petl. В качестве параметров передаем поле reviews нашего экземпляра класса. Затем удалим ненужные столбцы “isEdited” и “title”, воспользовавшись методом cutout() библиотеки Petl.

    if is_review:
        tbl = etl.fromdicts(last_reviews.reviews) # создание Petl таблицы
        tbl = etl.cutout(tbl, 'isEdited', 'title') # удаление ненужных столбоцв


    Теперь наша таблица выглядит так:

  3. Откроем файл date.json, который мы создавали в прошлом ноутбуке и получим его данные.

    with open('data.json') as f:
        date = json.load(f)

  4. Если дата из JSON файла совпадает с датой, что мы получили в блоке try, то добавим столбец с этой датой в нашу таблицу. Воспользуемся уже известным методом addfield() библиотеки Petl.

    if is_review:
        if date['LastUpdateDay'] == last_update_day:
            tbl = etl.addfield(tbl, 'UpdateDate', last_update_day)

  5. Аналогично примеру из первого ноутбука, экспортируем нашу таблицу в Excel файл.

    if is_review:
        tbl.toxlsx('visiology_mobile_reviews.xlsx', write_header=True, mode='replace') # экспорт в Excel


    Получим такую таблицу Еxcel:

  6. По аналогии с примером из первого ноутбука, устанавливаем подключение с базой данных PostgreSQL:

    engine = sqlalchemy.create_engine('postgresql://{user}:{user_password}@{url}:{port}/{database_name}') # подключение к базе данных 

    Где {user} - имя пользователя базы данных, {user_password} - пароль, {url} - адрес базы данных, {port} - порт, {database_name} - название базы данных, в которой будет создана таблица.

  7. Создаем таблицу Pandas так же, как и в первом ноутбуке.

    if is_review:    
        df = pd.DataFrame(columns=tbl[0], data=tbl[1:]) # создание DataFrame из petl-таблицы

  8. И экспортируем таблицу в Excel файл так же, как и в первом примере:

    # экспорт в базу данных
    if is_review:
        df.to_sql('visiology_mobile_reviews', engine, index=False, if_exists='replace',
                            dtype={
                                'userName': sqlalchemy.VARCHAR(30),
                                'rating': sqlalchemy.Integer(),
                                'review': sqlalchemy.Text(),
                                'date' : sqlalchemy.DateTime(),
                                'UpdateDate' : sqlalchemy.DateTime(),
                                'title': sqlalchemy.VARCHAR(50)
                            })

Мы получили 2 таблицы Еxcel и 2 таблицы в базе данных PostgreSQL.

Excel таблица, полученная с помощью App Store Connect API:
Excel таблица, полученная с помощью app-store-scraper:
Таблица в базе данных, полученная с помощью App Store Connect API:

Где 1 - это типы данных столбцов, 2 - данные таблицы.

Таблица в базе данных, полученная с помощью app-store-scraper:

Где 1 - это типы данных столбцов, 2 - данные таблицы.

Так выглядит дашборд, построенный из этих данных:

Полученные файлы можно брать и загружать в платформу через стандартный загрузчик. На этом работа в ViXtract завершена.🥳

  • Нет меток