1.2K anunciantes en línea
1.2K anunciantes en línea
¿No estas obteniendo los resultados esperados? ¿No recibes las visitas a tu sitio o las llamadas que deseabas? Aquí puedes obtener consejos para mejorar los resultados de tu cuenta de AdWords.
Guiame
favorite_border
Responder

AdWords Script para pausar keywords con bajo rendimiento de forma masiva en función de KPIs

[ Editado ]
Estudiante ✭ ✭ ✭
# 1
Estudiante ✭ ✭ ✭

Hace un tiempo, @enriquedelvalle me comentó que le parecería muy útil un script que le permitiera pausar keywords en función de varios KPIs, además de poder filtrar por cuentas (a nivel MCC), campañas, grupos de anuncios y keywords concretas. Me resultó una idea interesante y, aunque me ha costado sacar tiempo para ello, por fin lo he terminado y os lo quiero compartir. 

 

-->  Enlace a GitHub <--

Configuración previa del script

Antes de comenzar a repasar los KPIs, estas son las opciones que debemos configurar en primer lugar:

 

var mccLevelScript = true; // "true" value for MCC level script execution // "false" value for account level script execution
var RECIPIENT_EMAIL = "INSERT_HERE_YOUR_EMAIL"; // Enter an email if you want to receive script changes notifications
var timePeriod = 'ALL_TIME'; // Period of time to analyze. You can choose another value according to https://developers.google.com/adwords/scripts/docs/reference/adwordsapp/adwordsapp_keywordselector#forDateRange_1

 

  • mccLevelScript es la variable con la que podemos elegir si ejecutar el script a nivel de MCC (dejando el valor "true") o a nivel de cuenta, cambiar el valor por "false" (sin las comillas).
  • RECIPIENT_EMAIL contiene el email, o emails (separados por comas), destinatario al que notificar los resultados del script o si se ha producido algún error durante la ejecución. Si no se quiere recibir ninguna notificación, simplemente hay que dejar las 2 comillas sin ningún email dentro, y el punto y coma de la derecha: Es decir: "";
  • timePeriod es el periodo de tiempo que queremos analizar. Los posibles valores que se pueden utilizar son:

 

TODAY, YESTERDAY, LAST_7_DAYS, THIS_WEEK_SUN_TODAY, LAST_WEEK, LAST_14_DAYS, 
LAST_30_DAYS, LAST_BUSINESS_WEEK, LAST_WEEK_SUN_SAT, THIS_MONTH, LAST_MONTH, 
ALL_TIME.

 

También puedes elegir un rango de fechas concreto poniendo las fechas de inicio y fin de la siguiente manera: YYYYMMDD. Por ejemplo, para especificar el rango de fechas desde el 1 de enero de 2017 hasta el 31 de enero de 2017, sería así:

 

timePeriod = '20170101,20170131'; // Desde el 1 de enero de 2017 hasta el 31 de enero de 2017

 

Podéis consultar información más detallada en esta página de Google Developers.

 

Métricas y atributos de AdWords disponibles

 

Podemos filtrar las keywords que queremos pausar por los siguientes KPIs y atributos:

 

  • Concordancias de palabra clave. Podemos elegir cualquier combinación entre: amplia, frase y/o exacta. El script no distingue entre amplia y amplia modificada.
  • Nivel de calidad mínimo y máximo. También permite añadir o excluir las keywords con nivel de calidad neutro "--".

 

var keywordMatchTypes = ' [BROAD, PHRASE, EXACT] '; // Keyword Matches. Keep ONLY the matches you want to filter
var qualityScoreNull = false; // "true" value for including keywords without QS // "false" value for excluding them

// Integer values // NO decimals

var qualityScoreMin = null; 
var qualityScoreMax = null;
  • Clics mínimos y máximos
  • Impresiones mínimas y máximas
  • Conversiones mínimas y máximas
  • Coste acumulado mínimo y máximo
// Integer values // NO decimals

var impressionsMin = null; 
var impressionsMax = null; 

var clicksMin = null; 
var clicksMax = null; 

var conversionsMin = null; 
var conversionsMax = null; 

var costMin = null; 
var costMax = null; 

Como podéis ver en el propio código, se especifica qué valores puede tomar cada variable. En este caso, las variables de nivel de calidad, impresiones, clics, conversiones, coste acumulado y conversiones con vista, únicamente admiten números enteros (sin decimales).

 

  • Porcentaje de conversiones mínimo y máximo.
  • CTR mínimo y máximo
// Percentage values // 0% = 0.00, 1% = 0.01, 4,5% = 0.045, 10% = 0.10, 100% = 1.00, etc. 

var conversionRateMin = null; 
var conversionRateMax = null; 

var ctrMin = null; 
var ctrMax = null;

Estas variables admiten porcentajes escritos en formato decimal. Por ejemplo, 0% se representa como 0.00, un 5% como 0.05, un 53,47% como 0.5347, etcétera.

 

  • Posición media del anuncio mínima y máxima. En este caso, recordad que una posición media más grande, significa que el anuncio sale en peores posiciones y viceversa.
  • Coste por conversión mínimo y máximo.
// Number values with decimals allowed (if you want)

var cpaMin = null; 
var cpaMax = null; 

var avgPositionMin = null; 
var avgPositionMax = null;

 

Las métricas de CPA y Posición media admiten valores numéricos enteros y con decimales. El KPI de coste por conversión mínimo requiere de una aclaración:

 

Cuando tenemos un coste por conversión de CERO, AdWords lo trata (a efectos de filtrado) como si fuera "infinito" siempre que la keyword haya acumulado algún coste (aunque sea 0,01€). Por ejemplo, si filtramos por CPA mayor de 5€, el script también seleccionará aquellas keywords con CPA de 0€ que hayan acumulado algún coste, aunque ese coste no supere los 5€. Por este motivo, he programado el script para que se comporte de la siguiente manera:

 

  1. Si NO se utiliza el filtro de Coste mínimo acumulado, el script únicamente pausará las keywords sin conversiones que hayan acumulado, como mínimo, el mismo valor que el CPA. En el ejemplo anterior, de las keywords que tengan un CPA de 0€, únicamente pausaría las keywords con un coste mínimo acumulado de 5€ o más.
  2. Si se utiliza el filtro de Coste mínimo acumulado y este es menor que el de CPA, de las keywords con CPA 0€, pausará aquellas que tengan el coste mínimo acumulado que el usuario haya configurado. Por ejemplo, si configuro un coste mínimo de 3€ y un CPA mínimo de 5€, el script pausará las keywords con CPA mayor o igual a 5€ y, además, las keywords con CPA 0€ y coste acumulado de 3€ o más.

Filtrado por etiquetas

 

Otra de las opciones de filtrado que ofrece este script es el de filtrar por cuentas, si ejecutamos el script a nivel MCC, campañas, grupos de anuncios y keywords. Todos estos filtrados (tanto por etiquetas como por KPIs) son restrictivos. Es decir, se deben cumplir todas y cada una de las condiciones para que la keyword sea pausada. Tenemos las siguientes variables:

 

// FILTERING LABELS // If you don't want to filter by any label, DON'T make any change in its text // 'no label' = NO filtering

var mccLabelText = 'no label'; // Filter AdWords accounts which have this MCC Account label						  
var campaignLabelText = 'no label'; // Filter AdWords campaigns which have this campaign label
var adGroupLabelText = 'no label'; // Filter AdWords adGroups which have this adGroup label
var keywordLabelText = 'no label'; // Filter AdWords keywords which have this keyword label
  • mccLabelText contiene el texto de la etiqueta por la que queremos filtrar las cuentas publicitarias de un MCC.
  • campaignLabelText contiene el texto de la etiqueta para filtrar campañas.
  • adGroupLabelText, análogamente, contiene el texto de la etiqueta para filtrar grupos de anuncios.
  • keywordLabelText, por último, contiene el texto de la etiqueta para filtrar keywords.

 

El valor 'no label' hace que NO se utilice la etiqueta. Por defecto, todas las etiquetas tienen el filtrado deshabilitado (tienen el valor 'no label'). Se puede utilizar una misma etiqueta para filtrar tanto cuentas, como campañas, grupos de anuncios y/o keywords.

 

Únicamente hay que poner el texto en cada etiqueta que queramos utilizar. Por ejemplo, voy a filtrar cuentas mediante la etiqueta "EtiquetaMCC" y, también, voy a filtrar campañas y grupos de anuncios con la etiqueta "EtiquetaCuenta". NO voy a utilizar el filtrado por keywords. Quedaría así:

 

var mccLabelText = 'EtiquetaMCC'; // Filter AdWords accounts which have this MCC Account label						  
var campaignLabelText = 'EtiquetaCuenta'; // Filter AdWords campaigns which have this campaign label
var adGroupLabelText = 'EtiquetaCuenta'; // Filter AdWords adGroups which have this adGroup label
var keywordLabelText = 'no label'; // Filter AdWords keywords which have this keyword label

 

Aquí terminan las opciones de configuración para el usuario. Todas estas opciones, se encuentran agrupadas en la parte superior del script, justo a continuación del comentario inicial explicativo. Si, además, te interesa conocer el funcionamiento interno del script, sigue leyendo Guiño

 

Funcionamiento del AdWords Script

 

Construyendo la consulta de AdWords Query Language

 

La primera acción que realiza el script, incluso antes de comprobar si se debe ejecutar a nivel MCC o cuenta, es construir la consulta de AdWords Query Language (AWQL) con todas las opciones de filtrado configuradas por el usuario:

 

function main () {
  try {
   
   buildQuery();  // query building

 

Por seguir unas buenas prácticas en cuanto a la limpieza del código, se hace una llamada a la función buildQuery(), la cual contiene todas las comprobaciones necesarias para determinar qué parámetros incluir, y cuáles no.

 

function buildQuery (){

  if (impressionsMin != null){
    query = query + ' AND Impressions >= ' + impressionsMin;
  }

  if (impressionsMax != null){
    query = query + ' AND Impressions <= ' + impressionsMax;
  }

(...) // mas condiciones

  
  if (costMax != null){
    query = query + ' AND Cost <= ' + costMax;
  }  
   
  if (qualityScoreMin != null){
    queryQs = queryQs + ' AND QualityScore >= ' + qualityScoreMin;
  }
  
  if (qualityScoreMax != null){
    queryQs = queryQs + ' AND QualityScore <= ' + qualityScoreMax;
  }   
} // End of buildQuery function

 

query y queryQs son variables auxiliares declaradas al inicio del script

 

// Auxiliary Variables

var queryQs = "";                            
var query = 'KeywordMatchType IN '+keywordMatchTypes;
  • query contendrá todas las condiciones de filtrado por KPIs (los filtrados por etiquetas se realizan de otra forma más adelante), a excepción del filtrado por nivel de calidad.
  • queryQs contendrá la parte de la consulta AWQL correspondiente a los filtros de nivel de calidad mínimo y máximo. Lo aislo en esta variable porque AWQL no soporta el operador OR y, en el caso de querer incluir las keywords con nivel de calidad neutro "--", la codificación se haría bastante más compleja e ineficiente.

 

¿Ejecución a nivel MCC o cuenta?

 

Justo a continuación de lo anterior, el script comprueba si se desea ejecutar a nivel de MCC o a nivel de cuenta:

 

   if (mccLevelScript){
     var accountIds = getFilteredMccAccounts();
     var sequentialIds = accountIds.slice(0);
     if (accountIds.length > 0) {
       executeInSequence(sequentialIds, run);
     } else {
       body = body + "No Accounts after filtering by mccLabelText '" + mccLabelText + "' \n";
     }  
   } else {
     var aux = run();
   }

 

 

Función principal: run()

 

Una vez tenemos una cuenta publicitaria seleccionada (ya sea porque se ejecute a nivel cuenta o para cada cuenta a nivel MCC), se ejecutará la función "run()", la cual contiene el núcleo del script. Estos son los principales pasos que sigue la función:

 

Etiquetas de filtrado

 

Obtenemos las etiquetas existentes en la cuenta publicitaria

 

var labels = AdWordsApp.labels().get();

 

Las cotejamos con las etiquetas de filtrado para campañas, grupos de anuncios y keywords (ya que el filtrado de cuenta ya se ha hecho a nivel MCC) configuradas en el Script comprobando, para cada etiqueta de la cuenta, si coincide con alguna o varias de las etiquetas de filtrado:

 

 

  var labels = AdWordsApp.labels().get();
  var labelAux, labelNameAux;
  
  campaignLabelId = null;
  adGroupLabelId = null;
  keywordLabelId = null;
  
  while (labels.hasNext()){
    labelAux = labels.next();
    labelNameAux = labelAux.getName(); 
  
    if (labelNameAux == campaignLabelText) { 
      campaignLabelId = labelAux.getId(); 
    }
    
    if (labelNameAux == adGroupLabelText) { 
      adGroupLabelId = labelAux.getId(); 
    }    
 
    if (labelNameAux == keywordLabelText) { 
      keywordLabelId = labelAux.getId(); 
    }   
  }

 

Una vez comprobadas todas las etiquetas de la cuenta, verificamos que, para cada etiqueta de filtrado habilitada (tiene un valor distinto a 'no label'), hemos detectado si existe, o no, en la cuenta.

 

 

  if ((campaignLabelId == null) && (campaignLabelText != 'no label')) {
    body = body + "campaignLabelText Label doesn't exist in the account: " + accountName + "\n";
    body = body + "Paused Keywords: 0 \n";
    return false;
  }
  
  if ((adGroupLabelId == null) && (adGroupLabelText != 'no label')) {
    body = body + "adGroupLabelText Label doesn't exist in the account: " + accountName + "\n";
    body = body + "Paused Keywords: 0 \n";
    return false;
  }

  if ((keywordLabelId == null) && (keywordLabelText != 'no label')) {
    body = body + "keywordLabelText Label doesn't exist in the account: " + accountName + "\n";
    body = body + "Paused Keywords: 0 \n";
    return false;
  }

 

Si no hemos detectado en la cuenta publicitaria alguna de las etiquetas de filtrado habilitadas, el script finaliza su ejecución para esa cuenta, ya que esto quiere decir que existe una etiqueta de filtrado habilitada la cual no se ha aplicado en la cuenta, por lo que no tiene sentido seguir ejecutando el script en esa cuenta al no existir ninguna keyword que pueda cumplir todas las condiciones.

 

Filtrado de campañas

 

Comprobamos si tenemos habilitada la etiqueta de filtrado por campañas y, en caso afirmativo, obtenemos el Iterador de campañas filtradas por la etiqueta:

 

  var campaignIterator;
  var campaignIds = [];
  var campaignId;
  
  if (campaignLabelId != null) {
    campaignIterator = AdWordsApp.campaigns()
                                 .withCondition("LabelIds CONTAINS_ANY ["+campaignLabelId+"] ")
                                 .orderBy('CampaignId')
                                 .get();
    
  }

 

Acto seguido, comprobamos si el Iterador contiene, al menos, una campaña. En caso de no contener ninguna, de nuevo, finaliza la ejecución del script para esa cuenta ya que, en este caso, significaría que, aunque exista la etiqueta en la cuenta, no se ha aplicado a ninguna campaña, por lo que no tendremos ninguna keyword que cumpla con todas las condiciones.

 

    if (!campaignIterator.hasNext()){
        body = body + "campaignLabelText exists but is not applied to any campaign for this account \n";
        body = body + "Paused Keywords: 0 \n";
        return false; 
    }

 

En el caso de que no tengamos habilitada ninguna etiqueta de filtrado de campañas, obtenemos el Iterador con todas las campañas de la cuenta:

 

 else { // no campaignLabel filtering
    campaignIterator = AdWordsApp.campaigns()
                                 .orderBy('CampaignId')
                                 .get();
    }

 

Después de todas estas comprobaciones, ya con el Iterador de campañas procesado, recorremos todo el Iterador extrayendo los IDs de las campañas, para continuar con el filtrado en los siguientes pasos.

 

  while (campaignIterator.hasNext()){
    campaignId = campaignIterator.next().getId();
    campaignIds.push(campaignId);
  }

 

Filtrado de grupos de anuncios

 

Este apartado es prácticamente igual al anterior (filtrado de campañas), con una salvedad: se añade la siguiente condición adicional:

 

adGroupIterator = AdWordsApp.adGroups().withCondition("CampaignId IN [" + campaignIds + "]")

 

De esta manera, solo obtendremos los grupos de anuncios que, además de cumplir las otras condiciones, pertenezcan a alguna de las campañas ya filtradas en el paso anterior.  

 

Filtrado de keywords

 

La particularidad de este filtrado respecto a los anteriores está en el KPI de nivel de calidad. Por un lado, podemos encontrarnos con keywords con nivel de calidad numérico, para las cuales utilizamos la variable queryQs, que contiene la parte de la consulta AWQL correspondiente a este KPI como ya hemos visto anteriormente.

 

// ------------------------------- Getting filtered keywords -------------------------------------

  var keywordIterator, keywordIteratorAux;
  var keywordIds = []; 
  var keywordId;
  var adGroupId;
  
  if (keywordLabelId != null) {
    keywordIterator = AdWordsApp.keywords()
                                .withCondition("LabelIds CONTAINS_ANY [" + keywordLabelId + "] ")
                                .withCondition("AdGroupId IN [" + adGroupIds + "]")
								.withCondition(query + queryQs)
                                .forDateRange(timePeriod)
                                .orderBy('Id')
                                .get();

 

Por otro lado, si el usuario incluye las keywords con nivel de calidad neutro "--", debemos realizar otra consulta adicional:

 

    if (qualityScoreNull) {
      keywordIteratorAux = AdWordsApp.keywords()
                                     .withCondition("LabelIds CONTAINS_ANY [" + keywordLabelId + "] ")
                                     .withCondition("AdGroupId IN [" + adGroupIds + "]")
								     .withCondition(query + " AND HasQualityScore = 'FALSE' ")
                                     .forDateRange(timePeriod)
                                     .orderBy('Id')
                                     .get();    
    }

 

Y, una vez procesados los Iteradores, comprobamos si existe, al menos, una keyword que cumpla todas las condiciones. Si el resultado es negativo, es decir, el Iterador está vacío, el script finaliza su ejecución para esa cuenta, ya que significaría que, después de todos los filtrados, no hay ninguna keyword que cumpla todos los requisitos (de KPIs y etiquetas) para el periodo de tiempo seleccionado (mediante la variable timePeriod).

 

    if ((!keywordIterator.hasNext()) && (!qualityScoreNull || !keywordIteratorAux.hasNext())) {
      body = body + "keywordLabelText exists but is not applied to any filtered keyword for this account \n";
      body = body + "Paused Keywords: 0 \n";
      return false; 
    }

 

Preparación de la etiqueta de control de cambios del script

 

Después de aplicar todos los filtros, y antes de comenzar a pausar las keywords resultantes, comprobamos si la etiqueta scriptLabel (variable auxiliar) existe en la cuenta. Si no existe, la creará.

 

// Auxiliary Variables
var scriptLabel = "botScript did it"; // Script will add this label to the keywords paused by it // If label doesn't exist, the script will create it 

(...)

// ------------------------------- Checking script label -------------------------------------
  
  var label = AdWordsApp.labels().withCondition("LabelName = '"+ scriptLabel +"'").get();
  if (!label.hasNext()) {
    AdWordsApp.createLabel(scriptLabel, "Label created by botScript", "#FF3300");
    body = body + "Script Label '" + scriptLabel + "' didn't exist and was created in account: " + accountName + "\n";
  } else {
    body = body + "Script Label '" + scriptLabel + "' was already created in account: " + accountName + "\n";
  }

 

Pausar keywords resultantes del filtrado

 

Como último paso de la función run(), recorremos los Iteradores de keywords para pausarlas y aplicarles la etiqueta de control de cambios del script

 

// ------------------------------- Pausing filtered keywords -------------------------------------

  var keyword;
  var i = 0;
  
  while (keywordIterator.hasNext()) {
    keyword = keywordIterator.next();
    keyword.pause();
    keyword.applyLabel(scriptLabel);
    i++;
  } 

  if (qualityScoreNull) {
    while (keywordIteratorAux.hasNext()) {
      keyword = keywordIteratorAux.next();
      keyword.pause();
      keyword.applyLabel(scriptLabel);
      i++;
    }
  }

 

Enviar notificación del script por email

 

Una vez finaliza la ejecución del script en la cuenta, o cuentas si lo ejecutamos a nivel MCC, comprueba si se ha configurado un email para la notificación de resultados y, en caso afirmativo, envía la notificación, cuyo mensaje se construye de forma dinámica :

 

// Auxiliary Variables

var subject = "Results from Pause Keywords Script execution";
var body = "RESULTS from Pause Keywords Script execution: \n";
var subject_error = "ERROR during Script execution";

(...) 

function emailNotification (s, b, email){
  if (RECIPIENT_EMAIL != ""){
    MailApp.sendEmail(email, s, b);
  }
} // End of emailNotification function

(...) // funcion main

// ------------------------------- Send notification by email -------------------------------------

   emailNotification(subject, body, RECIPIENT_EMAIL);

 

Podéis consultar la última versión disponible del script en este enlace de GitHub. Espero que os sirva de ayuda o, al menos, de inspiración para desarrollar vuestros propios scripts.

 

Aunque lo "único" que haga el script sea pausar keywords, creo que se pueden hacer muchas variantes interesantes con unas sencillas modificaciones. Si tenéis cualquier corrección, comentario, sugerencia o idea a desarrollar, estaré encantado de leeros en los comentarios aquí abajo 😉

 

Expertos respondieronverified_user

AdWords Script para pausar keywords con bajo rendimiento de forma masiva en función de KPIs

Usuario Destacado
# 2
Usuario Destacado

Gracias por compartir Alberto!!!

Luis Cantero

AdWords Script para pausar keywords con bajo rendimiento de forma masiva en función de KPIs

Colaborador Principal
# 3
Colaborador Principal

Esta genial Alberto

Muchas gracias 

oscar abad