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.

Información básica sobre protección de datos: Responsable: Wemcor Servicios Web, SL. Finalidad: desarrollar la actividad comercial y enviar comunicaciones comerciales a los usuarios que lo autoricen. Legitimación: Consentimiento del interesado. Destinatarios: Wemcor y proveedores legitimados externos. Derechos: Acceder, rectificar y suprimir los datos, así como otros derechos como se explica en la información adicional. También se puede instar reclamación ante la Agencia Española de Protección de Datos.

Deja un comentario

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