Во многих приложениях вы наверняка встречали такой функционал, как запрос оценки для стора.

Без него вообще получить положительные оценки было бы довольно проблематично — вы бы получали лишь негативные оценки, в случае возникновения проблем у пользователя.


iOS Android

Зачем это вообще нужно с маркетинговой точки зрения?

Дело в том что пользователи принимают решение о установке приложения используя звезды и отзывы как социальные сигналы. Когда-то компания Аpptentive, даже посчитала каково влияние этих сигналов:


Было звезд Стало звёзд Прирост конверсии
* * * 30%
* * * * 340%
* * * * * 730%
* * * * * * 770%
* * * * * 280%
* * * * * * 540%
* * * * * * * 570%
* * * * * * * 89%
* * * * * * * * 97%
* * * * * * * * * 4%

То есть, даже успешность и стоимость вашей CPI кампании сильно зависят от рейтинга приложения.

Итак, задача ясна, инструмент получения оценок есть — осталось научиться им пользоваться.

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

Решение я когда то видел у «Медузы», а они в свою очередь, накопали его где-то в англоязычном интернете.

Построено оно на предварительной «сортировке» пользователей по настроению к приложению.

  1. Вам надо определить в своем продукте «Aha! момент»
  2. Буквально сразу, после осознания\получения ценности пользователем задать ему вопрос «Вам понравилось наше приложение?»
  3. В зависимости от ответа — или получить от пользователя оценку в виде рейтинга в строе, или собрать фидбек в следующем шаге

В целом схема выглядит так:

Процесс запроса оценки от пользователя

Это может быть блок встроенный в экран после основного функционала (например на странице «спасибо за заказ»), иди нижняя «шторка», или поп-ап. Выберите наименее раздражающий ваших пользователей вариант.

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

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

Если путь клиента по вашем сайту допускает логин\регистрацию через соцсети, получение системных писем (подтверждение почты), оплату через онлайн платежи — то вам знакома проблема «мусорных» источников в отчетах.

Во всех эти случаях клиент временно уходит на другой сайт, а возвращаясь — получает новый источник:

  • Платежный гейт (сайт банка, PayPal, webmoney и т.п.)
  • Почтовый сервис (Yandex, Mail.ru, Gmail и т.п.)
  • Сервис oAuth от соцсети (accounts.google.com, facebook.com и т.п.)

Т.к. отчеты строятся по последнему не-прямому источнику, получается что последний искусственно возникший источник создаёт новую сессию и перебивает рекламный источник приведший пользователя на сайт.

В отчетах Google Analytics все эти источники попадают в канал referral

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

Universal Analytics

Если источников ограниченное количество и они известны заранее — их необходимо добавить в Referral Exclusion List. Эта настройка находится в разделе Property -> Tracking Info.

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

Однако если переход с исключенного источника произошел до исключения, и его не перебьёт новый реальный источник — конверсии будут приписаны к нему даже после исключения.

Google Analytics 4

В новой версии GA это можно осуществить в настройках Web Stream.

Зайдите в Property -> Data Streams -> Web stream details -> More tagging settings -> List unwanted referrals

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

Например, чтобы исключить переходы с более чем 500 платежных гейтов добавленных поштучно в Universal Analytics, здесь хватило всего дишь одного исключения в виде регулярного выражения:

.*(pay|acs|bank|secure|3d|card).*\..*

А источники в виде всевозможных почтовых доменов, возникающие при переходе по ссылке подтверждения почты, можно исключить выражением:

.*mail.*\..*

Логин через сервис гугла исключается так:

.*accounts\.google\..*

Google Tag Manager

Если код GA у вас размещен у вас с помощью GTM, то данные о реферальном источнике можно перехватить и переписать еще до попадания данных в GA.

Раньше в GA был функционал исключения источника для визитов с меткой utm_nooverride. Сейчас этот функционал можно воссоздать с помощью GTM.

Создайте URL-переменную, с компонентом Query и ключом utm_nooverride — она будет искать такую метку во всех посещаемых адресах.

Далее, если вы используете Universal Analytics, создайте переменную Custom Javascript со следующим содержимым:

function () {
    if ('{{utm_nooverride}}' == '1') {
        return document.location.origin;
    } else {
        return document.referrer;
    }
}

В зависимости от наличия метки utm_nooverride в адресе страницы она будет определять выдавать ли адрес текущей страницы или источник перехода.

Далее в настройках тега Universal Analytics вам нужно будет добавить поле referrer для которого в качестве значения указать Custom Javascript переменную созданную на предыдущем шаге.

Чтобы добиться того же эффект используя GA4 внутри GTM, вам необходимо изменить содержимое переменной Custom Javascript на:

function () {
    if ('{{utm_nooverride}}' == '1') {
        return true
    } else {
        return false
    }
}

а название подменяемого поля на ignore_referrer

Теперь, для тех источников переходы с которых вы можете контролировать, в ссылку возвращающую посетителя на ваш сайт необходимо добавить метку ?utm_nooverride=1, и эти переходы перестанут создавать новые реферальные источники.

Рассказываю как создать огромную кампанию по ключевым словам конкурентов без ручного труда со сбором огромных массивов данных.

Для примера я возьму базу конкурентов собранную по методике описанной в другой моей статье — «Следим за конкурентами в Google Ads».

Плюсами такого подхода будут:

  • То что вы получите всех реальных конкурентов в поисковой рекламе, а не только тех которых вы сможете придумать\вспомнить.
  • То что у вас будут данные о масштабах пересечения ваших запросов, с запросами конкурентов — будет возможность отобрать самое релевантное.

Итак, данные лежат у меня в BigQuery. В таблицах нас интересуют вот эти поля:


Field name Type Mode
domain STRING NULLABLE
keyword STRING NULLABLE

Этого нам хватит чтобы создать структуру кампании.

В Advanced Apis нужно включить сервис BigQuery. Данные для скрипта мы получим с помощью вот такой функции:

function get_competitors() {
    var project_name = '**********', // название проекта
        dataset_name = '**********', // название датасета
        tables_mask = 'AU_IS_*';
    var queryRequest = BigQuery.newQueryRequest();
    queryRequest.query = 'SELECT COUNT(keyword) AS keys, domain ' +
        'FROM `' + project_name + '.' + dataset_name + '.AU_IS_*` ' +
        // даты впишите свои
        'WHERE SUBSTR(_TABLE_SUFFIX,-8) BETWEEN "20210121" AND "20210821" ' +
        'GROUP BY domain ' +
        'ORDER BY keys DESC ' +
        'LIMIT 500'; // топ-сколько конкурентов хотим взять?
    queryRequest.useLegacySql = false;
    var query = BigQuery.Jobs.query(queryRequest, project_name);
    var values = [];
    if (query.jobComplete) {
        for (var i = 0; i < query.rows.length; i++) {
            var row = query.rows[i];
            var domain = row.f[1].v;
            if (domain.indexOf('.') > -1) {
                values.push(domain);
            }
        }
    }
    return values;
}

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

Если вы запускаете кампанию с нуля и у вас нет подобной истории данных, её можно съэмулировать, например, с помощью функции сбора объявлений конкурентов в Key Collector, или любом другом похожем по функционалу сервисе.

Далее, переходим к созданию структуры кампании. Заранее необходимо создать лишь пустую кампанию, которую мы наполним группами с помощью следующей функции:

function buildGroups() {
    var campaignSelector = AdsApp
        .campaigns()
        .withCondition('Name CONTAINS_IGNORE_CASE "Competitors"'); // выбираем эту кампанию
    var campaignIterator = campaignSelector.get();
    while (campaignIterator.hasNext()) {
        var campaign = campaignIterator.next();
        var competitors = get_competitors(); // получаем домены конкурентов из BigQuery
        for (var i = 0; i < competitors.length; i++) {
            var adGroupSelector = campaign
                .adGroups()
                .withCondition('Name CONTAINS_IGNORE_CASE "' + competitors[i] + '"');
            var adGroupIterator = adGroupSelector.get();
            if (!adGroupIterator.hasNext()) {
                var adGroupBuilder = campaign.newAdGroupBuilder();
                var adGroupOperation = adGroupBuilder
                   .withName(competitors[i])
                   .withStatus("PAUSED")
                   .build();
                // создаём группу по домену конкурента, если такой в кампании ранее не было
            }
        }
    }
}

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

Теперь самое интересное — необходимо наполнить группы ключевыми словами конкурентов.

Я для этого взял данные сервиса API от SimilarWeb. У них есть подходящий метод (я использую Desktop Non-Branded Keywords), плюс их данные показались мне подходящими и качественно и количественно.

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

Вот функция для по получения фраз по домену:

function get_keys(domain) {
    var apiKey = '1234567890qwertyuiop'; // вписываем ключ API
    var MILLIS_PER_DAY = 1000 * 60 * 60 * 24,
        now = new Date(),
        fromDate = new Date(now.getTime() - 365 * MILLIS_PER_DAY),
        toDate = new Date(now.getTime() - 31 * MILLIS_PER_DAY),
        nowDate = new Date(now.getTime()),
        timeZone = AdWordsApp.currentAccount().getTimeZone(),
        fromformatDate = Utilities.formatDate(fromDate, timeZone, 'yyyy-MM'),
        toformatDate = Utilities.formatDate(toDate, timeZone, 'yyyy-MM');
    var url = 'https://api.similarweb.com/v1/website/' + domain +
        '/traffic-sources/nonbranded-search?api_key=' + apiKey +
        '&start_date=' + fromformatDate +
        '&end_date=' + toformatDate +
        '&country=world&limit=10000&main_domain_only=false&format=json';
    // Logger.log(url);
    var options = {
        muteHttpExceptions: true,
        method: 'GET',
        contentType: 'application/json'
    };
    var response = UrlFetchApp.fetch(url, options);
    var json = JSON.parse(response);
    if (!!json.meta.status) {
        if (json.meta.status == 'Error') {
            return null
        }
        if (json.meta.status == 'Success') {
            var keysArr = [];
            for (var k = 0; k < json.search.length; k++) {
                var key = json.search[k].search_term;
                if (key.split(' ').length < 5) {
                    // я решил брать только фразы короче 5 слов
                    keysArr.push(key);
                }
            }
            return keysArr;
        }
    }
}

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

Итак, функция получения ключей по домену у нас есть — давайте зальём их в группы.

Вот функция заполнения групп:

function buildKeys() {
    var campaignSelector = AdsApp
        .campaigns()
        .withCondition('Name CONTAINS_IGNORE_CASE "Competitors"');
    var campaignIterator = campaignSelector.get();
    while (campaignIterator.hasNext()) {
        var campaign = campaignIterator.next();
        var adGroupSelector = campaign
            .adGroups()
            .withCondition('Status = PAUSED')
            .withCondition('Name DOES_NOT_CONTAIN "_"')
            .orderBy('Name ASC');
        // Берем только всё ещё остановленные и не обработанные группы
        // При регулярном запуске скрипта это удобно
        // как только вы запустили группу - данные перестают обновляться
        var adGroupIterator = adGroupSelector.get();
        while (adGroupIterator.hasNext()) {
            var adGroup = adGroupIterator.next(),
                adGroupName = adGroup.getName();
            adGroup.setName('_' + adGroupName);
            // помечаем группу как обработанную
            var domain = adGroupName;
            Logger.log(domain);
            var keywordSelector = adGroup
                .keywords();
            var keywordIterator = keywordSelector.get();
            if (!keywordIterator.hasNext()) {
                var keys = get_keys(domain);
                // получаем фразы
                if ((!!keys) && (keys != null) && (keys.length > +0)) {
                    for (var n = 0; n < keys.length; n++) {
                        try {
                            var keywordOperation = adGroup.newKeywordBuilder()
                                .withText(keys[n])
                                .build();
                            // создаём фразу
                        } catch (e) {
                            Logger.log(e);
                        }
                    }
                }
            }
        }
    }
}

Тут есть особенности:

  1. Скрипт помечает группу как обработанную, даже если по домену не удалось получить фразы. Это нужно для случаев когда домен конкурента ещё новый и в spy-сервисе по нему еще не набралось данных.
  2. Для максимальной скорости ключи добавляются без какой либо трансформации и проверки успешности операции добавления.

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

С приведенными настройками, примерно за 3 дня авто-запуска скрипта у меня получилась кампания из 500 групп и ~800.000 фраз.

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