Wednesday, October 30, 2013

Disable Ads and Keywords For Out of Stock Items

As a follow up to my questions about cool scripts for Black Friday / Cyber Monday, today I put together a script to run through your urls and check if the item is out of stock on the website. If it is, we will pause the AdGroup.

This script has some of the same elements of my script on checking for broken links in your account, but it actually pulls the html source of each page and searches for a configurable string that lets it know when it is out of stock.

Let's walk through an example. I love some of the quirky gifts I find on ModCloth.com. But like any online store, some items go out of stock. Here is one I found while testing this script.
In order to get this script to work, I need to find out what is different about the page when it goes out of stock. If I right click and view the page source, and search for the work "stock", I can see a few different places where it is used. One of them is the following that says "in_stock":false.
That looks promising. I check on an in stock item and sure enough, "in_stock":true is on that page.

Alright, so now I know what text I need to use to fill in the OUT_OF_STOCK_TEXT variable in my code. Now each site is going to be a little different, so I have a simple script that uses the same url logic as the complete script that you can use for testing.
/************************************
* Quick Url In Stock/Out Of Stock Checker
* Version 1.0
* Created By: Russ Savage
* FreeAdWordsScripts.com
***********************************/
var STRIP_QUERY_STRING = true;
var WRAPPED_URLS = true;
var OUT_OF_STOCK_TEXT = 'The Text That Identifies An Out Of Stock Item Goes Here';
var URL_TO_TEST = 'Your Url To Check Goes Here';

function main() {
  var urlToTest = URL_TO_TEST;
  urlToTest = cleanUrl(urlToTest);
  var htmlCode = UrlFetchApp.fetch(urlToTest).getContentText();
  if(htmlCode.indexOf(OUT_OF_STOCK_TEXT) >= 0) {
    Logger.log('The item is out of stock.');
  } else {
    Logger.log('The item is in stock.');
  }
}

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;
}


Once you find some HTML text in the source of the landing page that identifies if an item is out of stock, you should be good to go on the full script. There are a few other options in the script that allow you to enable or disable various url manipulations in the script. And remember, this will pause only the Ads or Keywords that link to the page with the out of stock item.

Thanks,
Russ

/************************************
* 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 = true; // 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 OUT_OF_STOCK_TEXT = 'The Text That Identifies An Out Of Stock Item Goes Here';

function main() {
  var alreadyCheckedUrls = {};
  var iter = buildSelector().get();
  while(iter.hasNext()) {
    var entity = iter.next();
    var url = cleanUrl(entity.getDestinationUrl());
    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) {
  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() {
  var selector = (URL_LEVEL === 'Ad') ? AdWordsApp.ads() : AdWordsApp.keywords();
  selector = selector.withCondition('CampaignStatus != DELETED').withCondition('AdGroupStatus != DELETED');
  if(ONLY_ACTIVE) {
    selector = selector.withCondition('CampaignStatus = ENABLED').withCondition('Status = ENABLED');
    if(URL_LEVEL !== 'Ad') {
      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;
}