Як автоматизувати роботу PPC-фахівця?

Фахівці з контекстної реклами або власники інтернет-магазинів, які ведуть рекламний аккаунт своїми руками, щодня стикаються з необхідністю автоматизації контекстної реклами. У даній статті я розповім, як за допомогою невеликих і швидких налаштувань, можна звільнити себе від нудної монотонної роботи.

Головними помічниками в цій справі стануть два скрипта Google Ads. Перший скрипт дозволяє підставляти вартість товару в оголошення без подальшої модерації. Другий – зупиняє показ оголошень товарів, яких немає в наявності. Погодьтесь, це ж чудово!

За допомогою скриптів ми не тільки економимо свій час, але ще і позбавляємося від простою реклами, після внесених руками змін до оголошення.

Для установки даних скриптів нам не знадобляться особливі знання з області програмування. Досить все робити крок за кроком, як описано в цій статті.

Основні функції скриптів:

  • парсинг ціни товару на сайті і підстановка її в оголошення;
  • парсинг будь-якого іншого тексту на сторінці сайту і підстановка його у оголошення;
  • парсинг наявності товару і відключення оголошень, коли товари відсутні в продажу.

Сайти, для яких підійде даний пакет скриптів:

  • на сторінці товару вказана його ціна
  • на сторінці товару зазначено наявність товару

Що необхідно зробити?

1. Копіюємо скрипт підміни ціни:

var URL_LEVEL = 'Ad'; // Ad or Keyword
var ONLY_ACTIVE = false; // set to false for all ads or keywords
var CAMPAIGN_LABEL = ''; // set this if you want to only check campaigns with this label
var STRIP_QUERY_STRING = true; // set this to false if the stuff that comes after the question mark is important
var WRAPPED_URLS = true; // set this to true if you use a 3rd party like Marin or Kenshoo for managing you account
// This is the specific text to search for 
// on the page that indicates the item 
// is out of stock.
var LABEL_NAMES = ['"Price_Change"'];
var OUT_OF_STOCK_TEXT = 'Осталось 0 товаров';
var PRICE_TEXT_BEGIN = '';
var PRICE_TEXT_END = '';
var keywords = {};
 
function setKeywordPrice(keyword, price) {
  var keywordId = keyword.getId();
  if (keywords[keywordId]) {} else {
    Logger.log('Keyword: '+keyword+', Price: '+price);
    keyword.setAdParam(1, price);
    keywords[keywordId] = true;
  }
}

function setAdPrice(ad, price) {
  var keywords = ad.getAdGroup().keywords().get();
  while(keywords.hasNext()) {
    var keyword = keywords.next();
    setKeywordPrice(keyword, price);
    //Logger.log('Ad: '+ad+'; Price: '+price+'; Keyword: '+keyword);
  }
}

function main() {
  var alreadyCheckedUrls = {};
  var prices = {};
  var campIter = AdWordsApp.campaigns().get();
  while (campIter.hasNext()) {
    var camp = campIter.next();
  var adIter = buildSelector(camp, 'Ad');
  adIter = adIter.withCondition('LabelNames CONTAINS_ANY [' + LABEL_NAMES.join(',') + ']');
  adIter = adIter.get();
  //Logger.log(iter.totalNumEntities());
  while(adIter.hasNext()) {
      var entity = adIter.next();
      var url = entity.urls().getFinalUrl();
      if (url === null)
          continue;
      url = cleanUrl(url);
      if (prices[url]) {
        setAdPrice(entity, prices[url]);
        //Logger.log('Url: '+url+'; Price: '+prices[url]+'; Entity: '+entity);
      } else {
        var htmlCode;
        try {
          htmlCode = UrlFetchApp.fetch(url).getContentText();
        } catch(e) {
          Logger.log('There was an issue checking:'+url+', Skipping.');
          continue;
        }
        var priceStart = htmlCode.indexOf(PRICE_TEXT_BEGIN) + PRICE_TEXT_BEGIN.length;
        if(priceStart >= 0) {
          var priceEnd = htmlCode.indexOf(PRICE_TEXT_END, priceStart);
          prices[url] = htmlCode.substr(priceStart, priceEnd - priceStart).replace(/\D/, '');
          //setKeywordPrice(keyword, prices[url]);
          setAdPrice(entity, prices[url]);
          //Logger.log('Url: '+url+'; Price: '+prices[url]+'; Entity: '+entity);
        }
      }
      //Logger.log('Url: '+url+' price is '+prices[url]);
      
      if(alreadyCheckedUrls[url]) {
        if(alreadyCheckedUrls[url] === 'out of stock') {
          entity.pause();
          //keyword.pause();
        } else {
          entity.enable();
          //keyword.enable();
        }
      } else {
        var htmlCode;
        try {
          htmlCode = UrlFetchApp.fetch(url).getContentText();
        } catch(e) {
          Logger.log('There was an issue checking:'+url+', Skipping.');
          continue;
        }
        if(htmlCode.indexOf(OUT_OF_STOCK_TEXT) >= 0) {
          alreadyCheckedUrls[url] = 'out of stock';
          entity.pause();
        } else {
          alreadyCheckedUrls[url] = 'in stock';
          entity.enable();
        }
        Logger.log('Url: '+url+' is '+alreadyCheckedUrls[url]+'; price: '+prices[url]);
      }
  }
  }
}
 
function cleanUrl(url) {
  if(WRAPPED_URLS) {
    url = url.substr(url.lastIndexOf('http'));
    if(decodeURIComponent(url) !== url) {
      url = decodeURIComponent(url);
    }
  }
  if(STRIP_QUERY_STRING) {
    if(url.indexOf('?')>=0) {
      url = url.split('?')[0];
    }
  }
  if(url.indexOf('{') >= 0) {
    //Let's remove the value track parameters
    url = url.replace(/\{[0-9a-zA-Z]+\}/g,'');
  }
  return url;
}
 
function buildSelector(camp, url_level) {
  var selector = (url_level === 'Ad') ? camp.ads() : camp.keywords();
  return selector;
}

У оголошення додається динамічний параметр: {param1: 230} – де 230 будь-яке число, якщо використовується 4х значне, то вказати 4х значне. Перевірка додавання ціни тільки через пошук, в акаунті в оголошенні ціна не змінюватиметься. 2. Копіюємо скрипт перевірки наявності товарів:

/************************************
* Item Out Of Stock Checker
* Version 1.1
* ChangeLog v1.1 - Filtered out deleted Campaigns and AdGroups
* Created By: Russ Savage
* FreeAdWordsScripts.com
***********************************/
var URL_LEVEL = 'Ad'; // or Keyword
var ONLY_ACTIVE = false; // set to false for all ads or keywords
var CAMPAIGN_LABEL = 'CampaignLabel'; // set this if you want to only check campaigns with this label
var STRIP_QUERY_STRING = true; // set this to false if the stuff that comes after the question mark is important
var WRAPPED_URLS = true; // set this to true if you use a 3rd party like Marin or Kenshoo for managing you account
// This is the specific text to search for 
// on the page that indicates the item 
// is out of stock.
var OUT_OF_STOCK_TEXT = 'availability gray';
 
function main() {
  var alreadyCheckedUrls = {};
  var iter = buildSelector().get();
  while(iter.hasNext()) {
    var entity = iter.next();
	if(entity.urls().getFinalUrl()){
		var url = cleanUrl(entity.urls().getFinalUrl(), entity);
		if(alreadyCheckedUrls[url]) {
		  if(alreadyCheckedUrls[url] === 'out of stock') {
			entity.pause();
		  } else {
			entity.enable();
		  }
		} else {
		  var htmlCode;
		  try {
			htmlCode = UrlFetchApp.fetch(url).getContentText();
		  } catch(e) {
			Logger.log('There was an issue checking:'+url+', Skipping.');
			continue;
		  }
		  if(htmlCode.indexOf(OUT_OF_STOCK_TEXT) >= 0) {
			alreadyCheckedUrls[url] = 'out of stock';
			entity.pause();
		  } else {
			alreadyCheckedUrls[url] = 'in stock';
			entity.enable();
		  }
		}
		Logger.log('Url: '+url+' is '+alreadyCheckedUrls[url]);
	}
  }
}
 
function cleanUrl(url, entity) {
  if (url) {
	  if(WRAPPED_URLS) {
		url = url.substr(url.lastIndexOf('http'));
		if(decodeURIComponent(url) !== url) {
		  url = decodeURIComponent(url);
		}
	  }
	  if(STRIP_QUERY_STRING) {
		if(url.indexOf('?')>=0) {
		  url = url.split('?')[0];
		}
	  }
	  if(url.indexOf('{') >= 0) {
		//Let's remove the value track parameters
		url = url.replace(/\{[0-9a-zA-Z]+\}/g,'');
	  }
	}
	else Logger.log('Что-то пошло не так, в урле нулл: '+url+' '+ entity.getCampaign.getName+ ' -> ' +entity.getAdGroup.getName);
  return url;
}
 
function buildSelector() {
  //var selector = (URL_LEVEL === 'Keyword') ? AdWordsApp.ads() : AdWordsApp.keywords();
  var selector = (URL_LEVEL === 'Keyword') ? AdWordsApp.keywords() : AdWordsApp.ads();
  selector = selector.withCondition('CampaignStatus != DELETED').withCondition('AdGroupStatus != DELETED');
  if(ONLY_ACTIVE) {
    selector = selector.withCondition('CampaignStatus = ENABLED').withCondition('Status = ENABLED');
    if(URL_LEVEL !== 'Keyword') {
      selector = selector.withCondition('AdGroupStatus = ENABLED');
    }
  }
  if(CAMPAIGN_LABEL) {
    var label = AdWordsApp.labels().withCondition("Name = '"+CAMPAIGN_LABEL+"'").get().next();
    var campIter = label.campaigns().get();
    var campaignNames = [];
    while(campIter.hasNext()) {
      campaignNames.push(campIter.next().getName());
    }
    selector = selector.withCondition("CampaignName IN ['"+campaignNames.join("','")+"']");
  }
  return selector;
}

3. Помічаємо ярликами всі оголошення, в яких будемо використовувати підміну ціни, щоб наш скрипт зміг їх визначити.

Назву ярлика можна побачити на наступному скріншоті. Її можна замінити на свою.

4. Міняємо заголовки або опис в оголошеннях, які ми позначили раніше ярликом, шляхом додавання динамічної вставки в текст {param1: 230}. Це буде наша змінна, яка буде записуватися в масив даних Google Ads, а далі – підставлятися з масиву в текст.

Після двокрапки стоїть ціна 230 – це умовна ціна за замовчуванням, щоб розуміти кількість символів, яку вона займе. Після успішної установки скрипта і його запуску, ці дані автоматично заміняться на актуальну ціну з сайту

Важливо! В оголошеннях цього не видно, так як ціна буде підтягуватися з масиву даних усередині Google Ads та побачити її можна буде тільки в рекламній видачі.

5. Додаємо раніше скачаний скрипт в бібліотеку Google Ads. Просто створюєте новий скрипт і повністю вставляєте скопійований код в поле.

6. Визначаємо код, який включає в себе ціну на сайті. Для цього нам необхідно відкрити сторінку товару на сайті, з якої ми будемо аналізувати довільні ціну. Знайти на сторінці ціну і клацнути по ній правою кнопкою миші, де вибрати «Переглянути код елементу».

7. Знаходимо цей код і вказуємо для скрипта обидві його частини, між якими і знаходиться наша ціна. У нашому випадку код виглядає так:

<span data-el_name=”default_price”>3215 </span>.

Визначаємо початок коду – <span data-el_name = “default_price”> І кінець – </ span>..

Важливо! Всередині коду можуть перебувати не тільки цифри, а й текст на зразок «усього», «від», «грн», «usd» та ін. Різні символи і прогалини, які знаходяться до і після ціни, враховуються при її визначенні. Тому обов’язково вказуйте їх в скрипті, щоб нічого зайвого, крім ціни товару,  не витягнулося в наш параметр. Після того, як ми визначили початок і кінець ціни, додаємо ці частини коду в скрипт.

8. Натискаємо зберегти скрипт. Потім натискаємо кнопку «Перегляд» в правому нижньому куті екрана.

Якщо все зроблено правильно, то на вкладці «Журнал» можна буде спостерігати записи за отриманими цінами для різних оголошень і ключових слів.

Після того, як всі попередні операції були зроблені і відпрацювали без помилок, натискаємо кнопку «Зберегти».

Результати роботи підміни ціни можна побачити, якщо ввести цільовий запит товару в пошуку. За допомогою таких оголошень, перед кліком на рекламу ми даємо уявлення про ціну потенційного клієнту. Таким чином ми отримуємо більш гарячі переходи по рекламі і відкидаємо  кліки користувачів, для яких ці товари дуже дорогі.

Останнім етапом в налаштуванні скрипта буде створення розкладу, в залежності від частоти зміни цін на сайті.

Для цього переходимо в список скриптів аккаунта, для якого ми встановлювали наш скрипт, знаходимо його зі списку (якщо до цього у нас вже є в акаунті встановлені раніше скрипти) і натискаємо на кнопку редагування, що з’явилася  в стовпці «Частота». У віконці, що з’явилося вибираємо частоту і час спрацьовування для нашого скрипта.

Далі налаштуємо скрипт, який буде відключати оголошення, якщо товару немає в наявності на сайті.

Установка практично ідентична попередньму скрипту. Насамперед, помічаємо ярликом кампанії, в яких ми рекламуємо наші товари і будемо перевіряти їх наявність:

У нашому випадку ярлик буде з назвою «Test_In_Stock». За бажанням назву цього ярлика можна змінити. У цьому випадку не забудьте змінити її і в самому скрипті.

Далі переходимо на сторінку з нашим товаром, якого немає в наявності. Знаходимо на сторінці код елемента, який відповідає за наявність товару. У нашому випадку товари, які відсутні були без позначки «Немає в наявності». Замість неї на сторінці був шматок коду, який відповідав за кількість товару, що залишився  «Залишилося 0 товарів». Цей код, в результаті, ми використовували для визначення таких продуктів.

Після визначення тексту, який відповідає за наявність товару на складі, ми додаємо новий скрипт в акаунт і вставляємо раніше визначений текст в нього.

Після цього, даємо назву своєму скрипту і запускаємо його передперегляд:

Якщо все зроблено правильно, то на вкладці «Журнал» можна буде спостерігати записи по перевіреним оголошенням:

Після успішної установки, натискаємо на кнопку «Зберегти».

Не забудьте встановити розклад запуску скрипта, в залежності від частоти зміни наявності товарів на сайті. Установка розкладу частоти аналогічна скрипту динамічної підміни ціни в оголошенні.

Висновки. За допомогою таких простих і швидких налаштувань облікового запису, ми економимо собі тонну часу. Дані скрипти працюють на багатьох наших проектах інтернет-магазинів і виконують найбільшу частину рутинної роботи, в той час як ми займаємося більш корисними завданнями.

Ефективність кампаній, які використовують дані скрипти, набагато вище. І, звісно, ми економимо велику кількість грошових коштів клієнта не рекламуючи товари, яких немає в наявності.

Сподіваємося, що дані скрипти і для вас будуть дуже корисні.