Марат-блог
Услуги по продвижению и разработке сайта
Отправить заявку
Заказать обратный звонок

Спасибо, Ваша заявка принята.

В ближайшее время менеджер свяжется с Вами.

Главная » Контекстная реклама » Скрипты подмены цены и проверки нерабочих ссылок. Работа в AdWords
Скрипты подмены цены и проверки нерабочих ссылок. Работа в AdWords
Контекстная реклама
388
06 апреля 2018

Скрипты подмены цены и проверки нерабочих ссылок. Работа в AdWords

Нередко случается так, что в объявлениях нужно часто менять цену на товары. А если товаров становится слишком много, то это превращается в настоящую проблему. Сегодня поговорим о скриптах подмены цены и проверки нерабочих ссылок.

Когда-то у нас был клиент, для которого мы настраивали рекламу Адвордс и проводили A/B тестирование рекламы. Мы пришли к выводу, что реклама с ценой обладает лучшими показателями качества, нежели объявления, в которых стоимость не указана. Но товаров было более тысячи и ежедневно менять цифры было огромной проблемой.

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

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 = false; // 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 MIN=10;

var LABEL_NAMES = ['"SAPI3"'];

var OUT_OF_STOCK_TEXT = 'Нет в наличии';

var PRICE_TEXT_BEGIN = '<span class="price-new">';

var PRICE_TEXT_END = ' РіСЂРЅ</span>';

var keywords = {};

var arr=[];,

function setKeywordPrice(keyword, price) {

  var keywordId = keyword.getId();

  if (keywords[keywordId]) {

    Logger.log('Keyword: '+keyword+', Price: '+price);

    keyword.setAdParam(1, price);

    keywords[keywordId] = true;

  } else {

    Logger.log('Keyword: '+keyword+', Price: '+price);

    keyword.setAdParam(1, price);

    keywords[keywordId] = true;

  }

}

function setAdPrice(ad, price) {

  var keywords = ad.getAdGroup().keywords().get();

  var compain = ad.getAdGroup().getCampaign();

  while(keywords.hasNext()) {

    var keyword = keywords.next();

    setKeywordPrice(keyword, price);

    Logger.log('Ad: '+ad+'; Price: '+price+'; Keyword: '+keyword+'; c'+compain);

  }

}

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('Url2: '+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 arr=[];

        var cur=0;

        var ii=0;

        while(ii!=-1)

        {         

         ii=htmlCode.indexOf(PRICE_TEXT_BEGIN,cur);

          if(ii==-1){ break; }

         var priceStart = ii + PRICE_TEXT_BEGIN.length-1;

         cur=priceStart;

         //Logger.log('Price:'+priceStart);

         if(priceStart >= 0) {

          var priceEnd = htmlCode.indexOf(PRICE_TEXT_END, priceStart);

          prices[url] = htmlCode.substr(priceStart, priceEnd - priceStart).replace(/\D/, '');

          arr[arr.length]=prices[url];

           //Logger.log('price_arr:'+arr[arr.length-1]);

          //setKeywordPrice(keyword, prices[url]);

          //setAdPrice(entity, prices[url]);

          //Logger.log('Url: '+url+'; Price: '+prices[url]+'; Entity: '+entity);

         }

        }

        var i=0;

        var minimum=arr[i];

        if((minimum-'0')<MIN){ minimum=MIN; }

        for(i=0;i<arr.length;i++)

         {

           Logger.log('price:'+arr[i]);

           if( ((minimum-'0')>(arr[i]-'0'))&&(MIN<arr[i]) ){ minimum=arr[i]; }

         }

        prices[url]=minimum;

        setAdPrice(entity, minimum);

      }

      //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} в том месте, где и будет «подмениваться» цена.

 

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


Зайдите на указанный url и откройте в браузере код. Внимательно посмотрите, в каком именно теге находится стоимость товара.

 

После этого нужно добавить в скрипт необходимые теги: <span class=»price_element» itemprop=»price»>.

Может случиться и так, что на странице, которая расположена по указанному url, будет не один товар. В таком случае скрипт будет брать минимальную стоимость на этой странице в тегах. При необходимости возможно поставить фильтр на цену по нижнему порогу. Для этого укажите в переменной Min необходимое значение.

После этого добавьте скрипт в Адвордс.


Для проверки работы скрипта запустите его. Зайдите в «Журналы», там будут указаны записи цены, ключевики и url.

 

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

Учтите, что существуют ограничения по работе скриптов в Адвордс в пол часа. При отмечании ярлыками большого количества объявлений, будет вероятность не уложиться в данное время.

Теперь поговорим о втором скрипте. Он превратит сопровождение аккаунтов в более простую работу. Этот скрипт на уровне МСС осуществляет проверку всех ссылок в рекламе и отправляет на почту отчет с извещением о наличии нерабочей ссылки в том или ином аккаунте либо кампании.

Вот этот скрипт:

function main()

{

  var BAD_CODES = [404,500];

  var HTTP_OPTIONS = { muteHttpExceptions:true};

  var TO = ['zinchenko.sotnik@gmail.com','sotnik.developer@gmail.com'];

  var SUBJECT = 'Битые Url '+_getDateString();

  var already_checked = {};

  var bad_entities = [];

  //

 var accountIterator = MccApp.accounts()

      .withCondition("LabelNames CONTAINS '" + 'Work' + "'")

      .get();

  //цикл выводи анформации

   while (accountIterator.hasNext())

   {    

    var account = accountIterator.next();

    // Select the client account.

    MccApp.select(account);

    //******************************************//

      //Let's look at ads and keywords for urls

       var iters = [

      //For Ad Level Urls

      AdWordsApp.ads()

      .withCondition("Status = 'ENABLED'")

      .withCondition("AdGroupStatus = 'ENABLED'") //

      .withCondition("CampaignStatus = 'ENABLED'")

    //  .withCondition("Type = 'TEXT_AD'")

      .get(),

      //For Keyword Level Urls

      AdWordsApp.keywords()

      .withCondition("Status = 'ENABLED'")

      .withCondition("DestinationUrl != ''")

      .withCondition("AdGroupStatus = 'ENABLED'")

      .withCondition("CampaignStatus = 'ENABLED'")

      .get()

      ];

    

     for(var x in iters)

     {

       var iter = iters[x];

        while( iter.hasNext() )

        {

          var entity = iter.next();

          if( entity.urls().getFinalUrl() == null) { continue; }

          var url = entity.urls().getFinalUrl();

          if(url.indexOf('{') >= 0) {

           //Let's remove the value track parameters

           url = url.replace(/\{[0-9a-zA-Z]+\}/g,'');

          }

          if(already_checked[url]) { continue; }

          var response_code;

          try {

            //Logger.log("Testing url: "+url);

            response_code = UrlFetchApp.fetch(url, HTTP_OPTIONS).getResponseCode();

          } catch(e) {

            //Something is wrong here, we should know about it.

            bad_entities.push({e : entity, code : -1, name : account.getName()});

          }

          if(BAD_CODES.indexOf(response_code) >= 0) {

           //This entity has an issue.  Save it for later.

            bad_entities.push({e : entity, code : response_code, name : account.getName()});

          }else{ already_checked[url] = true; }

          Logger.log("Testing url: "+url+"  Code: "+response_code);

         

        }

     }

   }

 

      //******************************************//

     var column_names = ['Account','Type','CampaignName','AdGroupName','Id','Headline/KeywordText','ResponseCode','DestUrl'];

     var attachment = column_names.join(",")+"\n";

     for(var i in bad_entities) {

     attachment += _formatResults(bad_entities[i],",");

     }

     if(bad_entities.length > 0)

     {

      var options = { attachments: [Utilities.newBlob(attachment, 'text/csv', 'битые_ссылки_'+_getDateString()+'.csv')] };

      var email_body = "Битых ссылок " + bad_entities.length + ". Р’СЃРµ битые ссылки РІ attachment.";

     

      for(var i in TO) {

       MailApp.sendEmail(TO[i], SUBJECT, email_body, options);

      }

     } 

    //******************************************//

}

 

//Formats a row of results separated by SEP

function _formatResults(entity,SEP) {

  var e = entity.e;

  if(typeof(e['getHeadline']) != "undefined") {

    //this is an ad entity

    return [entity.name,

            "Ad",

            e.getCampaign().getName(),

            e.getAdGroup().getName(),

            e.getId(),

            e.getHeadline(),

            entity.code,

            e.urls().getFinalUrl(),

           ].join(SEP)+"\n";

  } else {

    // and this is a keyword

    return [entity.name,

            "Keyword",

            e.getCampaign().getName(),

            e.getAdGroup().getName(),

            e.getId(),

            e.getText(),

            entity.code,

            e.urls().getFinalUrl(),

           ].join(SEP)+"\n";

  }

}

//Helper function to format todays date

function _getDateString() {

  return Utilities.formatDate((new Date()), AdWordsApp.currentAccount().getTimeZone(), "yyyy-MM-dd");

}

Настройка скрипта происходит следующим образом.

Первым делом вы добавляете скрипт в аккаунт на уровне МСС. Затем вносите свою почту в скрипт.

Далее – отмечаете ярлыком Work, что указан в скрипте, аккаунты, которые находятся в работе. В том случае, если вам нужны все url в рекламе с редиректами, стоит добавить в скрипт код 301.

 

На этом работа окончена. Надеемся, скрипты были вам полезны! Упрощайте свою работу и автоматизируйте процессы. Успехов!