Автоматические скрипты помогают эффективно управлять ставками, стратегиями, временем показов и создавать отчеты. Скрипты пишут самостоятельно или используют готовые. В этой статье мы делимся с вами личными наработками. Для удобства внутри скриптов прикреплены комментарии.
Проверить ссылку объявления на ошибки
Этот автоматический скрипт ежедневно пробегает по всем рекламным кампаниям ваших клиентов и проверяет URL объявлений. Если он находит ошибки «404» и «300», вы получите оповещение на почту.
<code class="code language-markup" lang="html"><xmp> <script type="text/javascript"> function main() { var mail_to = 'здесь пишем e-mail специалиста'; var mccAccount = AdWordsApp.currentAccount(); //получаем проекты текущего аккаунта var childAccounts = MccApp.accounts().get(); var email = ''; //пробегаем по всем проектам аккаунта while (childAccounts.hasNext()) { //берем проект var childAccount = childAccounts.next(); //делаем его текущим MccApp.select(childAccount); var disabled_label = 'disabled-ad'; //----выполняем логику в рамках данного проекта------ if (childAccount.getStatsFor("LAST_14_DAYS").getClicks() == ) continue; AdWordsApp.createLabel(disabled_label, "Отключенные объявления в связи с тем, что не нашли цену на сайте", "red"); var checked_urls = {}; var campaignIterator = AdWordsApp.campaigns() .withCondition('Status = ENABLED') .get(); //пробегаем по всем активным кампаниям, while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); var groupIterator = campaign.adGroups() .withCondition('Status = ENABLED') .get(); while (groupIterator.hasNext()) { var group = groupIterator.next(); var adIterator = group.ads() .withCondition('Status = ENABLED') .get(); //пробегаем по всем группам while (adIterator.hasNext()) { var entity = adIterator.next(); var url = entity.urls().getFinalUrl(); //пропускаем, если урл пустой if (url === null) continue; //если объявление неактивно и нет специального лейбла, то пропускаем if (!entity.isEnabled()) continue; var ad_text = 'Группа: ' + group.getName() + ' Объявление: ' + entity.getId() + ' ' + (entity.getType() == 'TEXT_AD' ? '"' + entity.getHeadline() + '"' : (entity.getType() == 'EXPANDED_TEXT_AD' ? '"' + entity.getHeadlinePart1() + ' ' + entity.getHeadlinePart2() + '"' : '')); //очищаем урл от параметров, меток и т.д. url = clean_url(url); //если урл этого товара мы уже парсили, // то берем данные о цене и наличии из массива данных // чтобы снова не парсить if (checked_urls[url]) { //включили или отключили объявление в зависимости от наличия //или просто оповещаем на имейл (нужное раскомментировать) if (checked_urls[url]) { if (checked_urls[url] === 'error') { //ставим на паузу объявление //entity.pause(); //entity.applyLabel(disabled_label); email = email + '\n' + ' Ошибка! ' + childAccount.getName() + ' Кампания: "' + campaign.getName() + '" ' + ad_text + ' ' + ' Url: ' + url; } else { //иначе включаем и убираем метку //entity.removeLabel(disabled_label); //entity.enable(); } } } //если нет, то else { var html, headers, code; //парсим страницу товара try { var parsed = UrlFetchApp.fetch(url); //html = parsed.getContentText(); //headers = parsed.getHeaders(); code = parsed.getResponseCode(); } catch (e) { //отключаем объявление, если не смогли распарсить страницу //или просто оповещаем на имейл (нужное раскомментировать) //entity.pause(); //entity.applyLabel(disabled_label); email = email + '\n' + ' Ошибка! ' + childAccount.getName() + ' Кампания: "' + campaign.getName() + '" ' + ad_text + ' ' + ' Url: ' + url; checked_urls[url] = 'error'; } if (code >= 400) { //ставим на паузу объявление или просто пишем в лог checked_urls[url] = 'error'; //entity.pause(); //entity.applyLabel(disabled_label); } else { //активизируем объявление или пишем в лог checked_urls[url] = 'ok'; //entity.enable(); //entity.removeLabel(disabled_label); } if (code >= 300) { //тело письма email = email + '\n' + childAccount.getName() + ' Кампания: "' + campaign.getName() + '" ' + ad_text + ' ' + (code >= 400 ? ' ОШИБКА ' : '') + ' Url: ' + url } } } } } //--------------------------------------------------- } if (email != '') { //отправляем письмо MailApp.sendEmail(mail_to, "Оповещение об ошибках в урлах объявлениях AdWords", email); } //вернулись к MCC аккаунту MccApp.select(mccAccount); } function clean_url(url) { //урл может быть обернут враппером url = url.substr(url.lastIndexOf('http')); if (decodeURIComponent(url) !== url) { url = decodeURIComponent(url); } //убираем GET параметры if (url.indexOf('?') >= ) { url = url.split('?')[]; } //убираем скобки if (url.indexOf('{') >= ) { url = url.replace(/\{[-9a-zA-Z]+\}/g, ''); } return url; } </script> </xmp></code>
Отключить рекламу по праздникам
Скрипт работает сразу на несколько кампаний, полезен для mcc-аккаунтов. Вводите праздничные и перенесенные даты раз в году, остальное за вас сделает робот. В примере — праздничные даты за 2017 год.
<code class="code language-markup" lang="html"><xmp> <script type="text/javascript"> function main() { var mail_to = 'email первого специалиста'; //var mail_to = 'email второго специалиста'; var mccAccount = AdWordsApp.currentAccount(); var childAccounts = MccApp.accounts() .withIds(['здесь указываем идентификатор клиента mcc через запятую']) .get(); var email = ""; var disabled_label = "holidays-disabled-ad"; //пробегаемся по всем проектам аккаунта while (childAccounts.hasNext()) { //берем проект var childAccount = childAccounts.next(); //делаем его текущим MccApp.select(childAccount); //создаем на аккаунте лейбл disabled_label, если его нет var labels1 = childAccount.labels().get(); var label_exist = false; while (labels1.hasNext()) { var label = labels1.next(); if(label.getName() == disabled_label) { label_exist = true; } } if(!label_exist) AdWordsApp.createLabel(disabled_label, "Отключенная кампания в праздничный день", "red"); var campaignIterator = AdWordsApp.campaigns().get(); //пробегаем по всем кампаниям, while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); if(!have_sunday(campaign)) { if(is_holiday()) { //помечаем и отключаем if(!campaign.isEnabled()) continue; campaign.applyLabel(disabled_label); campaign.pause(); email = email +('Клиент: ' + childAccount.getName() + ' Кампания: '+campaign.getName()+' отключена\n'); } else { //если помечено, включаем и снимаем пометку var labels = campaign.labels().get(); while (labels.hasNext()) { var label = labels.next(); if(label.getName() == disabled_label) { campaign.removeLabel(disabled_label); campaign.enable(); email = email +('Клиент: ' + childAccount.getName() + ' Кампания: '+campaign.getName()+' включена\n'); } } } } } ///--------------------------------------------------- } if(email != '') { MailApp.sendEmail(mail_to, "Оповещение об отключении/включении в праздники AdWords", email); } //вернулись к MCC аккаунту MccApp.select(mccAccount); } function have_sunday(campaign) { var schedules = campaign.targeting().adSchedules().get(); while (schedules.hasNext()) { var schedule = schedules.next(); if(schedule.getDayOfWeek() == 'SUNDAY') return true; } return false; } function is_holiday() { //Список праздничных дат: 1-8 января, 23 февраля, 8 марта, 1 мая, 8-9 мая, 12 июня, 6 ноября var holidays = ['01.01','02.01','03.01','04.01','05.01','06.01','07.01','08.01','23.02','08.03','01.05','08.05','09.05','12.06','06.11']; var date = Utilities.formatDate(new Date, 'Europe/Moscow', "dd.MM"); if(in_array(date,holidays)) return true; return false; } function in_array(needle, haystack, strict) { var found = false, key, strict = !!strict; for (key in haystack) { if ((strict && haystack[key] === needle) || (!strict && haystack[key] == needle)) { found = true; break; } } return found; } </script> </xmp></code>
Поменять типы соответствия ключей
Если вы до сих пор работаете с ключевыми словами вручную, воспользуйтесь этим скриптом. Он переводит ключевые слова из широкого соответствия в модификатор широкого соответствия, во фразовое соответствие или в точное соответствие, а также добавляет или исключает исходные ключи.
<code class="code language-markup" lang="html"><xmp> <script type="text/javascript"> function main() { /* Режимы обработки: 1. Добавляет новую фразу с плюсом С УДАЛЕНИЕМ исходного купить дом -> +купить +дом 2. Сделать копию фразы с плюсами БЕЗ УДАЛЕНИЯ исходного купить дом -> купить дом +купить +дом 3. Сделать копию фразы в 3 вариациях С УДАЛЕНИЕМ исходного купить дом -> +купить +дом "купить дом" [купить дом] 4. Сделать копию фразы в 3 вариациях БЕЗ УДАЛЕНИЯ исходного купить дом -> купить дом +купить +дом "купить дом" [купить дом] 5. Добавляет новую фразу без плюса С УДАЛЕНИЕМ исходного +купить +дом -> купить дом купить +дом -> купить дом */ // ! Выберите режим обработки: var TYPE = 5; //! Обрабатывать фразы, у которых кликов (за посл. месяц) БОЛЬШЕ, чем: var CLICKS = -1; var adGroupIterator = AdWordsApp.adGroups() .withCondition("CampaignStatus = ENABLED") .withCondition("CampaignName DOES_NOT_CONTAIN_IGNORE_CASE 'КМС'") .withCondition("AdGroupStatus = ENABLED") .get(); Logger.log('Всего найдено активных групп: ' + adGroupIterator.totalNumEntities()); while (adGroupIterator.hasNext()) { var adGroup = adGroupIterator.next(); var newKeywords = []; Logger.log('Группа: ' + adGroup.getName()); var keywordIterator = adGroup.keywords().withCondition("Status = ENABLED").get(); while (keywordIterator.hasNext()) { var keyword = keywordIterator.next(); var kname = keyword.getText(); //пропускаем кейворды, если кликов за последние 30 дней меньше CLICKS (указан выше) if (keyword.getStatsFor('LAST_30_DAYS').getClicks() <= CLICKS) continue; if (kname.indexOf('+') == -1 && kname.indexOf('"') == -1 && kname.indexOf('[') == -1 && kname.indexOf(']')) { Logger.log('Найден ключ без спецсимволов: ' + kname); if (TYPE == 1 || TYPE == 2 || TYPE == 3 || TYPE == 4) { newKeywords.push('+' + kname.replace(/ /g, ' +').replace(/-/g, ' +')); } if (TYPE == 3 || TYPE == 4) { newKeywords.push('"' + kname + '"'.replace(/-/g, ' ')); newKeywords.push('[' + kname + ']'.replace(/-/g, ' ')); } if (TYPE == 1 || TYPE == 3) { keyword.remove(); Logger.log('Удален!'); } } if(TYPE == 5 && kname.indexOf('+') != -1) { var newkey = kname.replace(/\+/g, ''); Logger.log('Найден ключ с плюсиками: ' + kname + ' и создан новый '+newkey); newKeywords.push(newkey); keyword.remove(); Logger.log('Ключ '+kname+' удален!'); } } if (newKeywords.length) Logger.log('Добавлены следующие новые ключи:'); for (var i = ; i < newKeywords.length; i++) { Logger.log(newKeywords[i]); var keywordOperation = adGroup.newKeywordBuilder() .withText(newKeywords[i]) .build(); //var keyword = keywordOperation.getResult(); //тут можно новому keyword указать цену и прочее, //а можно дождаться, когда запустится скрипт-биддер и всё сделает сам } } } </script> </xmp></code>
Составить отчет по ключам и группам
Скрипт заполняет в Google Таблицы показатели по всем кампаниям, группам объявлений и объявлениям:
• число показов;
• количество кликов;
• CTR;
• стоимость;
• показатель качества;
• цена клика.
<code class="code language-markup" lang="html"><xmp> <script type="text/javascript"> function main() { var impressions_sum_total = , clicks_sum_total = , impressions_proc_sum_total = , qs_sum_total = , qs_e_sum_total = , cost_sum_total = , ctr_sum_total = , topcpc_sum_total = , firstpagecpc_sum_total = , count_total = ; var max = [, , , , , , , , , , , , ]; //открываем гуглдок var GOOGLE_DOC_URL = "самостоятельно создайте пустой google spreadsheet с правом редактирования по ссылке и здесь оставьте ссылку на него var spreadsheet = SpreadsheetApp.openByUrl(GOOGLE_DOC_URL); //формируем название вкладки из текущей даты вида "2017.01.21" var currentDate = new Date(); var day = currentDate.getDate(); var month = currentDate.getMonth() + 1; var year = currentDate.getFullYear(); var name = day + "." + (month < 10 ? '' + month : month) + "." + year; //если вкладка с таким именем уже есть, удаляем её var cursheet = spreadsheet.getActiveSheet(); if (cursheet) if (cursheet.getName() == name) spreadsheet.deleteSheet(cursheet); //добавялем новую вкладку вида "2017.01.21" var newSheet = spreadsheet.insertSheet(name, ); //колонки + их форматирование var columnNames = ["Кампания", "Группа объявлений", "Фраза", "Клики", "Показы", "Показов %", "QS фразы", "Расширенный QS", "CTR", "Cost", "TopOfPageCpc", "FirstPageCpc", "isPaused"]; var headersRange = newSheet.getRange(1, 1, 1, columnNames.length); headersRange.setFontWeight("bold"); headersRange.setFontSize(12); headersRange.setBorder(false, false, true, false, false, false); headersRange.setValues([columnNames]); newSheet.setFrozenRows(1); var campaignIterator = AdWordsApp.campaigns() .withCondition('Status = ENABLED') .get(); //пробегаемся по кампаниям while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); var count_campaign = ; var keywords = []; Logger.log('Кампания: ' + campaign.getName()); var adGroupIterator = campaign.adGroups() .withCondition('Status = ENABLED') .get(); var impressions_campaign_sum = , clicks_campaign_sum = , impressions_proc_campaign_sum = , qs_campaign_sum = , qs_e_campaign_sum = , cost_campaign_sum = , ctr_campaign_sum = , topcpc_campaign_sum = , firstpagecpc_campaign_sum = ; //пробегаемся по группам while (adGroupIterator.hasNext()) { var adGroup = adGroupIterator.next(); var campaign_name = (adGroup.getCampaign() ? adGroup.getCampaign().getName() : ''); Logger.log('Группа: ' + adGroup.getName()); keywords = []; var keywordIterator = adGroup.keywords().get(); //считаем показы кейвордов всей группы за неделю // (чтобы потом посчитать % показов для каждого кейворда) var all_impressions = ; while (keywordIterator.hasNext()) { var keyword = keywordIterator.next(); var stats = keyword.getStatsFor("LAST_WEEK"); all_impressions = all_impressions + stats.getImpressions(); } //собираем данные по каждому кейворду группы в массив, // дабы после отсортировать и вывести в гуглдок var keywordIterator = adGroup.keywords().get(); var impressions_sum = , clicks_sum = , impressions_proc_sum = , qs_sum = , qs_e_sum = , cost_sum = , ctr_sum = , topcpc_sum = , firstpagecpc_sum = , ctr_not_zero_count = ; while (keywordIterator.hasNext()) { var keyword = keywordIterator.next(); //берем статус за последнюю неделю var stats = keyword.getStatsFor("LAST_WEEK"); //если показов не было, то кейворд не берем в гуглдок var impressions = stats.getImpressions(); if (!impressions) continue; //берем клики var clicks = stats.getClicks(); //считаем процент показов var impressions_proc = Math.round((all_impressions > ? impressions / all_impressions * 100 : ) * 100) / 100; //берем CTR и Cost var ctr = Math.round(stats.getCtr() * 1000) / 10; var cost = Math.round(stats.getCost()); //берем QualityScore var qs = keyword.getQualityScore(); //процент QS var qs_e = Math.round(impressions_proc * qs); //берем MaxCpc var maxcpc = keyword.getMaxCpc(); //берем TopOfPageCpc var topcpc = keyword.getTopOfPageCpc(); //берем FirstPageCpc var firstpagecpc = keyword.getFirstPageCpc(); //берем статус var ispaused = keyword.isPaused(); var keytext = keyword.getText(); //для фраз, начинающихся на +, добавляем в начало кавычку, // иначе гуглдок расценит это как спецсимвол и не отобразит данные в ячейке if (keytext[] == '+') keytext = "'" + keytext; //добавляем все эти данные в массив keywords.push([campaign_name, adGroup.getName(), keytext, clicks, impressions, impressions_proc, qs, qs_e, ctr, cost, topcpc, firstpagecpc, ispaused]); //считаем сумму показателей для вывода ИТОГО, // среднего и максимального для каждой колонки, для группы и кампании clicks_sum += clicks; impressions_sum += impressions; impressions_proc_sum += impressions_proc; qs_sum += qs; qs_e_sum += qs_e; ctr_sum += ctr; if(ctr > ) ctr_not_zero_count++; cost_sum += cost; firstpagecpc_sum += firstpagecpc; topcpc_sum += topcpc; clicks_campaign_sum += clicks; impressions_campaign_sum += impressions; impressions_proc_campaign_sum += impressions_proc; qs_campaign_sum += qs; qs_e_campaign_sum += qs_e; ctr_campaign_sum += ctr; cost_campaign_sum += cost; firstpagecpc_campaign_sum += firstpagecpc; topcpc_campaign_sum += topcpc; clicks_sum_total += clicks; impressions_sum_total += impressions; impressions_proc_sum_total += impressions_proc; qs_sum_total += qs; qs_e_sum_total += qs_e; ctr_sum_total += ctr; cost_sum_total += cost; firstpagecpc_sum_total += firstpagecpc; topcpc_sum_total += topcpc; if (clicks > max[4]) max[4] = clicks; if (impressions > max[5]) max[5] = impressions; if (impressions_proc > max[6]) max[6] = impressions_proc; if (qs > max[7]) max[7] = qs; if (qs_e > max[8]) max[8] = qs_e; if (ctr > max[9]) max[9] = ctr; if (cost > max[10]) max[10] = cost; if (topcpc > max[11]) max[11] = topcpc; if (firstpagecpc > max[12]) max[12] = firstpagecpc; } //если в группе нашлись кейворды с показами, сортируем их и выводим в гуглдок всю пачку var count = keywords.length; count_total += count; count_campaign += count; if (count > ) { Logger.log('Сортируем ключи: ' + keywords.length + ' шт.'); keywords = usort(keywords, impressionsDesc); for (var i in keywords) { Logger.log('Пишу: ' + keywords[i][2]); newSheet.appendRow(keywords[i]); } //вывод ИТОГО для группы newSheet.appendRow(['Итого для группы ' + adGroup.getName() + ':', '', '', clicks_sum, Math.round(impressions_sum), Math.round(impressions_proc_sum), Math.round(qs_sum / count), Math.round(qs_e_sum / count), (ctr_not_zero_count ? Math.round(ctr_sum / ctr_not_zero_count) : ), Math.round(cost_sum), Math.round(topcpc_sum / count), Math.round(firstpagecpc_sum / count)]); newSheet.getRange(newSheet.getLastRow(), 1, 1, newSheet.getLastColumn()) .setBackground('#ACDFF7').setFontWeight("bold"); } } if (impressions_campaign_sum) { //вывод ИТОГО для кампании newSheet.appendRow(['Итого для кампании ' + campaign.getName() + ':', '', '', clicks_campaign_sum, Math.round(impressions_campaign_sum), Math.round(impressions_proc_campaign_sum), Math.round(qs_campaign_sum / count_campaign), Math.round(qs_e_campaign_sum / count_campaign), Math.round(ctr_campaign_sum / count_campaign), Math.round(cost_campaign_sum), Math.round(topcpc_campaign_sum / count_campaign), Math.round(firstpagecpc_campaign_sum / count_campaign)]); newSheet.getRange(newSheet.getLastRow(), 1, 1, newSheet.getLastColumn()) .setBackground('#5CDFF7').setFontWeight("bold"); newSheet.appendRow([' ']); impressions_campaign_sum = , clicks_campaign_sum = , impressions_proc_campaign_sum = , qs_campaign_sum = , qs_e_campaign_sum = , cost_campaign_sum = , ctr_campaign_sum = , topcpc_campaign_sum = , firstpagecpc_campaign_sum = ; } } //вывод общего ИТОГО newSheet.insertRowAfter(1); newSheet.appendRow(['Общий итог:', '', '', clicks_sum_total, Math.round(impressions_sum_total), Math.round(impressions_proc_sum_total), Math.round(qs_sum_total / count_total), Math.round(qs_e_sum_total / count_total), Math.round(ctr_sum_total / count_total), Math.round(cost_sum_total), Math.round(topcpc_sum_total / count_total), Math.round(firstpagecpc_sum_total / count_total)]); newSheet.getRange(newSheet.getLastRow(), 1, 1, newSheet.getLastColumn()) .setBackground('#A685E0').setFontWeight("bold").moveTo(newSheet.getRange("A2")); //перекраска колонок for (var j = 4; j <= 12; j++) { var colors = ['#f8696b', '#faa274', '#ccdd81', '#9acd7e']; if (j > 9) colors = colors.reverse(); for (var i = 2; i <= newSheet.getLastRow() - 2; i++) { var cell = newSheet.getRange(i, j); if (cell.getBackground() != '#ffffff') continue; for (var part = 1; part < 5; part++) { if (cell.getValue() !== '') if ((cell.getValue() < max[j] / 4 * part && cell.getValue() >= max[j] / 4 * (part - 1)) || (part == 4 && cell.getValue() == max[j])) cell.setBackground(colors[part - 1]); } } } for (var i = 2; i <= newSheet.getLastRow() - 2; i++) { var cell = newSheet.getRange(i, 13); if (cell.getBackground() != '#ffffff') continue; if (cell.getValue()) cell.setBackground('#f8696b'); } //автоматический размер колонок newSheet.autoResizeColumn(1).autoResizeColumn(2) .autoResizeColumn(3).autoResizeColumn(7) .autoResizeColumn(8).autoResizeColumn(9) .autoResizeColumn(10).autoResizeColumn(11); } //функции для сортировки массива function usort(inputArr, sorter) { var valArr = [], k = '', i = , strictForIn = false, populateArr = {}; if (typeof sorter === 'string') { sorter = this[sorter]; } else if (Object.prototype.toString.call(sorter) === '[object Array]') { sorter = this[sorter[]][sorter[1]]; } this.php_js = this.php_js || {}; this.php_js.ini = this.php_js.ini || {}; strictForIn = this.php_js.ini['phpjs.strictForIn'] && this.php_js.ini['phpjs.strictForIn'].local_value && this.php_js .ini['phpjs.strictForIn'].local_value !== 'off'; populateArr = strictForIn ? inputArr : populateArr; for (k in inputArr) { if (inputArr.hasOwnProperty(k)) { valArr.push(inputArr[k]); if (strictForIn) { delete inputArr[k]; } } } try { valArr.sort(sorter); } catch (e) { return false; } for (i = ; i < valArr.length; i++) { populateArr[i] = valArr[i]; } return strictForIn || populateArr; } function impressionsDesc(a, b) { return b[3] - a[3]; } </script> </xmp></code>
Это не самые популярные скрипты, а лишь те, которые используем мы. С ними проще следить за изменениями в аккаунтах и они экономят время — мы больше анализируем и управляем кампаниями. Еще примеры вы найдете на официальном сайте Google AdWords.