Рассказываю как создать огромную кампанию по ключевым словам конкурентов без ручного труда со сбором огромных массивов данных.
Для примера я возьму базу конкурентов собранную по методике описанной в другой моей статье — «Следим за конкурентами в 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 = AdsApp.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);
}
}
}
}
}
}
}
Тут есть особенности:
- Скрипт помечает группу как обработанную, даже если по домену не удалось получить фразы. Это нужно для случаев когда домен конкурента ещё новый и в spy-сервисе по нему еще не набралось данных.
- Для максимальной скорости ключи добавляются без какой либо трансформации и проверки успешности операции добавления.
Следует пояснить, почему я уже второй раз упоминаю про скорость. Дело в том что по крупным конкурентам у вас может получиться очень много фраз. Настолько много что добавление фраз всего лишь в одну группу не будет успевать отрабатывать за отведенные 30 минут.
С приведенными настройками, примерно за 3 дня авто-запуска скрипта у меня получилась кампания из 500 групп и ~800.000 фраз.
В дальнейшем фразы можно отсмотреть, продублировать в нужных типа соответствия, а также добавить объявления в группы и запустить группу. Или просто держать кампанию на паузе, и постепенно исследуя забирать оттуда удачные фразы в ваши рабочие кампании.