Как создать масштабную кампанию на основе ключевых слов конкурентов без ручного труда и собрать огромные объемы данных?
Для примера я возьму базу данных конкурентов, собранную по методу, описанному в другой моей статье — «Как шпионить за конкурентами в Google Ads«.
Преимуществами такого подхода будут:
- Дело в том, что вы получите всех реальных конкурентов в поисковой рекламе, а не только тех, о которых сможете подумать\запомнить.
- Тот факт, что у вас будут данные о степени пересечения ваших запросов с запросами конкурентов‘ позволит вам выбрать наиболее релевантные из них.
Итак, данные находятся в моем BigQuery. В таблицах нас интересуют эти поля:
Field name | Type | Mode |
---|---|---|
domain | STRING | NULLABLE |
keyword | STRING | NULLABLE |
Этого достаточно для создания структуры кампании.
Вам необходимо включить службу BigQuery в расширенных API. Мы получим данные для скрипта, используя следующую функцию:
function get_competitors() {
var project_name = '**********', // Project name
dataset_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_*` ' +
// enter your dates
'WHERE SUBSTR(_TABLE_SUFFIX,-8) BETWEEN "20210121" AND "20210821" ' +
'GROUP BY domain ' +
'ORDER BY keys DESC ' +
'LIMIT 500'; // top-how many competitors do we want to select?
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"'); // select this campaign
var campaignIterator = campaignSelector.get();
while (campaignIterator.hasNext()) {
var campaign = campaignIterator.next();
var competitors = get_competitors(); // get competitors' domains from 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();
// create a ad group by the competitor's domain, if there was no such group in the campaign before
}
}
}
}
Вы можете регулярно запускать эту функцию — она будет добавлять группы‘ если в базе данных появятся новые записи конкурентов.
Теперь самое интересное — вам нужно заполнить свои группы ключевыми словами ваших конкурентов.
Для этого я взял данные из сервиса SimilarWeb API. У них есть подходящий метод (я использую Desktop Non-Branded Keywords), плюс их данные показались мне подходящими как качественно, так и количественно.
В принципе, вы можете воспользоваться любым сервисом, предоставляющим данные поискового запроса по домену.
Вот функция для получения ключевых слов по доменам с помощью программного обеспечения:
function get_keys(domain) {
var apiKey = '1234567890qwertyuiop'; // API key
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) {
// I decided to take only phrases shorter than 5 words
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');
// Take only ad groups that are still stopped and not processed
// When running the script regularly, this is convenient
// as soon as you start the ad group - the data stops updating
var adGroupIterator = adGroupSelector.get();
while (adGroupIterator.hasNext()) {
var adGroup = adGroupIterator.next(),
adGroupName = adGroup.getName();
adGroup.setName('_' + adGroupName);
// mark the group as processed
var domain = adGroupName;
Logger.log(domain);
var keywordSelector = adGroup
.keywords();
var keywordIterator = keywordSelector.get();
if (!keywordIterator.hasNext()) {
var keys = get_keys(domain);
// get phrases
if ((!!keys) && (keys != null) && (keys.length > +0)) {
for (var n = 0; n < keys.length; n++) {
try {
var keywordOperation = adGroup.newKeywordBuilder()
.withText(keys[n])
.build();
// create phrase
} catch (e) {
Logger.log(e);
}
}
}
}
}
}
}
Здесь есть несколько особенностей:
- Скрипт помечает группу как обработанную, даже если получить ключевые слова для домена не удалось. Это необходимо для случаев, когда домен конкурента еще новый и шпионский сервис еще не собрал данные о нем.
- Для максимальной скорости ключи добавляются без каких-либо преобразований или проверки успешности операции добавления.
Я должен объяснить, почему я уже второй раз упоминаю скорость. Дело в том, что вы можете получить много ключевых слов для крупных конкурентов. Настолько, что добавление фраз только в одну группу не успеет отработать отведенные 30 минут.
С вышеуказанными настройками примерно за 3 дня автоматического запуска скрипта я получил кампанию из 500 групп и ~ 800 000 фраз.
В будущем вы сможете просматривать ключевые слова, дублировать их в соответствующих типах соответствия, а также добавлять объявления в группы и создавать группу. Или просто приостановите кампанию и постепенно проводите исследования, чтобы использовать оттуда удачные фразы в своих рабочих кампаниях.