Нередко случается так, что в объявлениях нужно часто менять цену на товары. А если товаров становится слишком много, то это превращается в настоящую проблему. Сегодня поговорим о скриптах подмены цены и проверки нерабочих ссылок.
Когда-то у нас был клиент, для которого мы настраивали рекламу Адвордс и проводили 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.
На этом работа окончена. Надеемся, скрипты были вам полезны! Упрощайте свою работу и автоматизируйте процессы. Успехов!