Comprobar URL rotas de anuncios de Adwords

Cuantas veces nos ha pasado a todos, que se nos cae la web, actualizamos un sitio o cambiamos las URL y nos olvidamos de los anuncios de Adwords, estamos pagando por tráfico que mandamos a páginas que no existen!

Con los siguiente Scripts para Mcc, comprobaremos automáticamente que todas las URL de nuestros anuncios funcionen a la perfección.

  1. Para MCC (Centro de clientes)
/******************************************
* Monitor Broken Links Using MCC Level Scripts
* Version 1.3
* Changelog v1.3
* - Added previous version of report api to script until
* I update my urls.
* Changelog v1.2
* - Fixing INVALID_PREDICATE_ENUM_VALUE
* Changelog v1.1
* - Stopped timeouts
* Created By: Russ Savage
* FreeAdWordsScripts.com
******************************************/
var SCRIPT_NAME = 'Broken Url Checker';
var LOG_LEVEL = 'error'; //change this to debug if you want more logging
var NOTIFY = ['arnau@erigin.com'];
var SPREADSHEET_PREFIX = 'Broken Url Details'; // A timestamp is appended
var NOTIFY_ON_ERROR = ['email+error@example.com'];
var STRIP_QUERY_STRING = true; //Drop everything after the ? in the url to speed things up
var REPORT_ON_REDIRECTS = true; //If you want to be able to track 301s and 302, turn this on
var VALID_RESPONSE_CODES = [200,301,302];
var URLS_CHECKED_FILE_NAME = 'UrlsAlreadyChecked-'+AdWordsApp.currentAccount().getCustomerId()+'.json';
var DONE_LABEL_PREFIX = 'All Urls Checked - ';

function main() {
 MccApp.accounts().withLimit(50).executeInParallel('checkUrls', 'reportResults');
}

function checkUrls() {
 try {
 debug('Processing account: '+AdWordsApp.currentAccount().getName());

 debug('Checking to see if we finished processing for today.');
 var dateStr = Utilities.formatDate(new Date(), AdWordsApp.currentAccount().getTimeZone(), 'yyyy-MM-dd');
 var finishedLabelName = DONE_LABEL_PREFIX+dateStr;
 var alreadyDone = AdWordsApp.labels().withCondition("Name = '"+finishedLabelName+"'").get().hasNext();
 if(alreadyDone) {
 info('All urls have been checked for today.');
 return '';
 }
 var labelIter = AdWordsApp.labels().withCondition("Name STARTS_WITH '"+DONE_LABEL_PREFIX+"'").get();
 while(labelIter.hasNext()) { labelIter.next().remove(); }

 debug('Checking for previous urls.');
 var urlsAlreadyChecked = readValidUrlsFromJSON();
 info('Found '+Object.keys(urlsAlreadyChecked).length+' urls already checked.');

 var toReportKeywords = [];
 var toReportAds = [];
 var didExitEarly = false;
 var keywordUrls = getKeywordUrls();
 for(var key in keywordUrls) {
 var kwRow = keywordUrls[key];
 var url = cleanUrl(kwRow.DestinationUrl);
 verifyUrl(kwRow,url,urlsAlreadyChecked,toReportKeywords);
 if(shouldExitEarly()) { didExitEarly = true; break; }
 }
 if(!didExitEarly) {
 var adUrls = getAdUrls();
 for(var i in adUrls) {
 var adRow = adUrls[i];
 if(adRow.CreativeDestinationUrl) {
 var url = cleanUrl(adRow.CreativeDestinationUrl);
 verifyUrl(adRow,url,urlsAlreadyChecked,toReportAds);
 }
 if(adRow.ImageAdUrl) {
 var url = cleanUrl(adRow.CreativeDestinationUrl);
 verifyUrl(adRow,url,urlsAlreadyChecked,toReportAds);
 }
 if(shouldExitEarly()) { didExitEarly = true; break; }
 }
 }
 var returnData = {
 accountId : AdWordsApp.currentAccount().getCustomerId(),
 accountName : AdWordsApp.currentAccount().getName(),
 uniqueUrlsChecked : Object.keys(urlsAlreadyChecked).length,
 brokenKeywords : toReportKeywords,
 brokenAds : toReportAds,
 didExitEarly : didExitEarly
 };
 if(didExitEarly) {
 writeValidUrlsToJSON(urlsAlreadyChecked);
 } else {
 AdWordsApp.createLabel(finishedLabelName, 'Label created by '+SCRIPT_NAME, '#C0C0C0');
 writeValidUrlsToJSON({});
 }
 return JSON.stringify(returnData);
 } catch(e) {
 // This error handling helps notify you when things don't work out well.
 error(e);
 if(MailApp.getRemainingDailyQuota() >= NOTIFY_ON_ERROR.length) {
 var acctName = AdWordsApp.currentAccount().getName();
 var acctId = AdWordsApp.currentAccount().getCustomerId();
 for(var i in NOTIFY_ON_ERROR) {
 info('Sending mail to: '+NOTIFY_ON_ERROR[i]);
 MailApp.sendEmail(NOTIFY_ON_ERROR[i], 'ERROR: '+SCRIPT_NAME+' - '+acctName+' - ('+acctId+')', e);
 }
 } else {
 error('Out of email quota for the day. Sending a carrier pigeon.');
 }
 return '';
 }

 function shouldExitEarly() {
 return (AdWordsApp.getExecutionInfo().getRemainingTime() < 60);
 }

 function verifyUrl(row,url,urlsAlreadyChecked,toReport) {
 if(!urlsAlreadyChecked[url]) {
 info('Checking url: ' + url);
 var urlCheckResults = checkUrl(url);
 if(!urlCheckResults.isValid) {
 row['cleanUrl'] = url;
 row['responseCode'] = urlCheckResults.responseCode;
 toReport.push(row);
 }
 urlsAlreadyChecked[url] = urlCheckResults;
 } else {
 if(!urlsAlreadyChecked[url].isValid) {
 row['cleanUrl'] = url;
 row['responseCode'] = urlsAlreadyChecked[url].responseCode;
 toReport.push(row);
 }
 }
 }

 function checkUrl(url) {
 var retVal = { responseCode : -1, isValid: false };
 var httpOptions = {
 muteHttpExceptions:true,
 followRedirects:(!REPORT_ON_REDIRECTS)
 };
 try {
 retVal.responseCode = UrlFetchApp.fetch(url, httpOptions).getResponseCode();
 retVal.isValid = isValidResponseCode(retVal.responseCode);
 } catch(e) {
 warn(e.message);
 //Something is wrong here, we should know about it.
 retVal.isValid = false;
 }
 return retVal;
 }

 function isValidResponseCode(resp) {
 return (VALID_RESPONSE_CODES.indexOf(resp) >= 0);
 }

 //Clean the url of query strings and valuetrack params
 function cleanUrl(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(/{[^}]*}/g,'');
 }
 return url;
 }

 //Use the reporting API to pull this information because it is super fast.
 //The documentation for this is here: http://goo.gl/IfMb31
 function getKeywordUrls() {
 var OPTIONS = { includeZeroImpressions : true, apiVersion: 'v201409' };
 var cols = ['CampaignId','CampaignName',
 'AdGroupId','AdGroupName',
 'Id','KeywordText','KeywordMatchType',
 'IsNegative','DestinationUrl','Impressions'];
 var report = 'KEYWORDS_PERFORMANCE_REPORT';
 var query = ['select',cols.join(','),'from',report,
 'where CampaignStatus = ENABLED',
 'and AdGroupStatus = ENABLED',
 'and Status = ENABLED',
 'during','LAST_7_DAYS'].join(' ');
 var results = {};
 var reportIter = AdWordsApp.report(query, OPTIONS).rows();
 while(reportIter.hasNext()) {
 var row = reportIter.next();
 if(row.IsNegative === 'true') { continue; }
 if(!row.DestinationUrl) { continue; }
 if(row.KeywordMatchType === 'Exact') {
 row.KeywordText = ['[',row.KeywordText,']'].join('');
 } else if(row.KeywordMatchType === 'Phrase') {
 row.KeywordText = ['"',row.KeywordText,'"'].join('');
 }
 var rowKey = [row.CampaignId,row.AdGroupId,row.Id].join('-');
 results[rowKey] = row;
 }
 return results;
 }

 //Use the reporting API to pull this information because it is super fast.
 //The documentation for this is here: http://goo.gl/8RHTBj
 function getAdUrls() {
 var OPTIONS = { includeZeroImpressions : true, apiVersion: 'v201409' };
 var cols = ['CampaignId','CampaignName',
 'AdGroupId','AdGroupName',
 'AdType',
 'Id','Headline','Description1','Description2','DisplayUrl',
 'CreativeDestinationUrl','ImageAdUrl','Impressions'];
 var report = 'AD_PERFORMANCE_REPORT';
 var query = ['select',cols.join(','),'from',report,
 'where CampaignStatus = ENABLED',
 'and AdGroupStatus = ENABLED',
 'and Status = ENABLED',
 'during','TODAY'].join(' ');
 var results = {};
 var reportIter = AdWordsApp.report(query, OPTIONS).rows();
 while(reportIter.hasNext()) {
 var row = reportIter.next();
 if(!row.CreativeDestinationUrl || !row.ImageAdUrl) { continue; }
 var rowKey = [row.CampaignId,row.AdGroupId,row.Id].join('-');
 if(row.ImageAdUrl !== '--') {
 row.ImageAdUrl = ('https://tpc.googlesyndication.com/pageadimg/imgad?id='+row.ImageAdUrl);
 } else {
 row.ImageAdUrl = '';
 }
 results[rowKey] = row;
 }
 return results;
 }

 //This function quickly writes the url data to a file
 //that can be loaded again for the next run
 function writeValidUrlsToJSON(toWrite) {
 var file = getFile(URLS_CHECKED_FILE_NAME,false);
 file.setContent(JSON.stringify(toWrite));
 }

 //And this loads that stored file and converts it to an object
 function readValidUrlsFromJSON() {
 var file = getFile(URLS_CHECKED_FILE_NAME,false);
 var fileData = file.getBlob().getDataAsString();
 if(fileData) {
 return JSON.parse(fileData);
 } else {
 return {};
 }
 }
}

//This is the callback function that collects all the data from the scripts
//that were run in parallel on each account. More details can be found here:
// http://goo.gl/BvOPZo
function reportResults(responses) {
 var summaryEmailData = [];
 var dateTimeStr = Utilities.formatDate(new Date(), AdWordsApp.currentAccount().getTimeZone(), 'yyyy-MM-dd HH:m:s');
 var spreadsheetName = SPREADSHEET_PREFIX+' - '+dateTimeStr;
 for(var i in responses) {
 if(!responses[i].getReturnValue()) { continue; }
 var res = JSON.parse(responses[i].getReturnValue());
 var sheetUrl = writeResultsToSpreadsheet(res,spreadsheetName);
 summaryEmailData.push({accountId:res.accountId,
 accountName:res.accountName,
 didExitEarly:res.didExitEarly,
 uniqueUrlsChecked:res.uniqueUrlsChecked,
 numBrokenKeywords:res.brokenKeywords.length,
 numBrokenAds:res.brokenAds.length,
 sheetUrl: sheetUrl});
 }
 if(summaryEmailData.length > 0) {
 sendSummaryEmail(summaryEmailData);
 }

 function writeResultsToSpreadsheet(res,name) {
 var file = getFile(name,true);
 var spreadsheet;
 var maxRetries = 0;
 while(maxRetries < 3) {
 try {
 spreadsheet = SpreadsheetApp.openById(file.getId());
 break;
 } catch(e) {
 maxRetries++;
 Utilities.sleep(1000);
 }
 }
 if(!spreadsheet) { throw 'Could not open file: '+name; }
 if(spreadsheet.getSheetByName('Sheet1')) {
 spreadsheet.getSheetByName('Sheet1').setName(res.accountId);
 }
 var sheet = spreadsheet.getSheetByName(res.accountId);
 if(!sheet) {
 sheet = spreadsheet.insertSheet(res.accountId, spreadsheet.getSheets().length);
 }
 var toWrite = [['Type','Clean Url','Response Code','Campaign Name','AdGroup Name','Text','Full Url']];
 for(var i in res.brokenKeywords) {
 var row = res.brokenKeywords[i];
 toWrite.push(['Keyword',
 row.cleanUrl,
 row.responseCode,
 row.CampaignName,
 row.AdGroupName,
 row.KeywordText,
 row.DestinationUrl]);
 }
 for(var i in res.brokenAds) {
 var row = res.brokenAds[i];
 toWrite.push([row.AdType,
 row.cleanUrl,
 row.responseCode,
 row.CampaignName,
 row.AdGroupName,
 (row.Headline) ? [row.Headline,row.Description1,row.Description2,row.DisplayUrl].join('|') : '',
 (row.CreativeDestinationUrl) ? row.CreativeDestinationUrl : row.ImageAdUrl]);
 }
 var lastRow = sheet.getLastRow();
 var numRows = sheet.getMaxRows();
 if((numRows-lastRow) < toWrite.length) {
 sheet.insertRowsAfter(lastRow,toWrite.length-numRows+lastRow);
 }
 var range = sheet.getRange(lastRow+1,1,toWrite.length,toWrite[0].length);
 range.setValues(toWrite);
 if((sheet.getMaxColumns() - sheet.getLastColumn()) > 0) {
 sheet.deleteColumns(sheet.getLastColumn()+1, sheet.getMaxColumns() - sheet.getLastColumn());
 }
 file = DriveApp.getFileById(spreadsheet.getId());
 try {
 file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
 } catch(e) {
 file.setSharing(DriveApp.Access.DOMAIN_WITH_LINK, DriveApp.Permission.VIEW);
 }
 //This gives you a link directly to the spreadsheet sheet.
 return (spreadsheet.getUrl() + '#gid=' + sheet.getSheetId());
 }

 //This function builds the summary email and sends it to the people in
 //the NOTIFY list
 function sendSummaryEmail(summaryEmailData) {
 var subject = SCRIPT_NAME+' Summary Results';
 var body = subject;
 var htmlBody = '<html><body>'+subject;
 htmlBody += '<br/ >Should strip query strings: '+STRIP_QUERY_STRING;
 htmlBody += '<br/ >Report on redirects: '+REPORT_ON_REDIRECTS;
 htmlBody += '<br/ >Valid response codes: '+VALID_RESPONSE_CODES;
 htmlBody += '<br/ ><br/ >';
 htmlBody += '<table border="1" width="95%" style="border-collapse:collapse;">';
 htmlBody += '<tr>';
 htmlBody += '<td align="left"><b>Acct Id</b></td>';
 htmlBody += '<td align="left"><b>Acct Name</b></td>';
 htmlBody += '<td align="left"><b>Exited Early</b></td>';
 htmlBody += '<td align="center"><b>Unique Urls Checked</b></td>';
 htmlBody += '<td align="center"><b># Broken Keyword Urls</b></td>';
 htmlBody += '<td align="center"><b># Broken Ad Urls</b></td>';
 htmlBody += '<td align="center"><b>Full Report</b></td>';
 htmlBody += '</tr>';
 for(var i in summaryEmailData) {
 var row = summaryEmailData[i];
 htmlBody += '<tr><td align="left">'+ row.accountId +
 '</td><td align="left">' + row.accountName +
 '</td><td align="left">' + row.didExitEarly +
 '</td><td align="center">' + row.uniqueUrlsChecked +
 '</td><td align="center">' + row.numBrokenKeywords +
 '</td><td align="center">' + row.numBrokenAds +
 '</td><td align="left"><a href="'+row.sheetUrl+'">' + 'Show Details' +
 '</a></td></tr>';
 }
 htmlBody += '</table>';
 htmlBody += '<br/ >';
 htmlBody += Utilities.formatDate(new Date(),AdWordsApp.currentAccount().getTimeZone(),'MMMM dd, yyyy @ hh:mma z');
 htmlBody += '. Completed. '+Object.keys(summaryEmailData).length+' Accounts checked.';
 htmlBody += '</body></html>';
 var options = { htmlBody : htmlBody };
 for(var i in NOTIFY) {
 MailApp.sendEmail(NOTIFY[i], subject, body, options);
 }
 }
}

//This function finds a given file on Google Drive
//If it does not exist, it creates a new file
//if isSpreadsheet is set, it will create a new spreadsheet
//otherwise, it creates a text file.
function getFile(fileName,isSpreadsheet) {
 var maxRetries = 0;
 var errors = [];
 while(maxRetries < 3) {
 try {
 var fileIter = DriveApp.getFilesByName(fileName);
 if(!fileIter.hasNext()) {
 info('Could not find file: '+fileName+' on Google Drive. Creating new file.');
 if(isSpreadsheet) {
 return SpreadsheetApp.create(fileName);
 } else {
 return DriveApp.createFile(fileName,'');
 }
 } else {
 return fileIter.next();
 }
 } catch(e) {
 errors.push(e);
 maxRetries++;
 Utilities.sleep(1000);
 }
 }
 if(maxRetries == 3) {
 throw errors.join('. ');
 }
}

//Some functions to help with logging
var LOG_LEVELS = { 'error':1, 'warn':2, 'info':3, 'debug':4 };
function error(msg) { if(LOG_LEVELS['error'] <= LOG_LEVELS[LOG_LEVEL]) { log('ERROR',msg); } }
function warn(msg) { if(LOG_LEVELS['warn'] <= LOG_LEVELS[LOG_LEVEL]) { log('WARN' ,msg); } }
function info(msg) { if(LOG_LEVELS['info'] <= LOG_LEVELS[LOG_LEVEL]) { log('INFO' ,msg); } }
function debug(msg) { if(LOG_LEVELS['debug'] <= LOG_LEVELS[LOG_LEVEL]) { log('DEBUG',msg); } }
function log(type,msg) { Logger.log(type + ' - ' + msg); }

Espero que este Script de Google Adwords os sea de ultilidad.

Deja un comentario

Artículo añadido al carrito.
0 artículos - 0,00