Etiquetador de palabras clave: cuenta única

Ícono de herramientas

Realizar cambios a gran escala en tus palabras clave, como inhabilitar aquellas que tienen bajo rendimiento, puede ser tedioso. La secuencia de comandos del etiquetador de palabras clave simplifica esta tarea, ya que aplica etiquetas automáticamente a las palabras clave según las reglas que definas. Una vez que se aplican las etiquetas, puedes filtrar fácilmente las palabras clave en la IU de Google Ads y aplicar los cambios deseados, o puedes realizar cambios en las palabras clave con otra secuencia de comandos.

Estos son algunos ejemplos de cómo se puede utilizar esta secuencia de comandos:

  • Etiqueta las palabras clave que recientemente tuvieron un rendimiento inferior. Luego, puede filtrar estas palabras clave en la IU de Google Ads y modificar sus ofertas, o detenerlas o quitarlas.
  • Etiqueta las palabras clave que contengan un nombre propio asociado con tu marca, como el nombre de uno de tus productos. Más adelante, puedes usar esta etiqueta para segmentar la vista de palabras clave y comprender mejor tu rendimiento.
  • Etiqueta las palabras clave para las que deseas cambiar las ofertas. Luego, puedes revisar las etiquetas en la IU de Google Ads antes de realizar los cambios mediante las ediciones masivas.
  • Etiquetar todas las palabras clave con su nivel de calidad Luego, etiqueta las palabras clave cuyo nivel de calidad actual no coincide con su nivel de calidad anterior (como se refleja en la etiqueta original) para que puedas revisarlos.
  • Cualquier combinación de lo anterior y más.

La secuencia de comandos envía un correo electrónico con un vínculo a una hoja de cálculo cuando se etiquetan palabras clave nuevas.

Cómo funciona

La secuencia de comandos te permite definir reglas con un formato simple con estos campos:

  • conditions: Es una lista opcional de condiciones KeywordSelector. Por ejemplo, estas condiciones pueden seleccionar palabras clave según sus estadísticas de rendimiento.
  • dateRange: Es un período KeywordSelector opcional para usar cuando se seleccionan campos de estadísticas. Si se omite, se utiliza un período predeterminado en la secuencia de comandos.
  • filter: Es una función opcional para aplicar a cada palabra clave después de que se recupera. Esto permite definir mejor si las palabras clave coinciden con la regla. El filtro se puede usar para expresar una lógica más compleja en ciertos campos de palabras clave que no se pueden expresar en conditions.
  • labelName: Es un nombre de etiqueta requerido para aplicar a cualquier palabra clave que coincida con la regla y no tenga esa etiqueta.

Esta es una regla de ejemplo para identificar y etiquetar las palabras clave de bajo rendimiento asociadas con tu marca:

{
  conditions: [
    'Ctr < 0.02',
    'AverageCpc > 1',
  ],
  dateRange: 'LAST_7_DAYS',
  filter: function(keyword) {
    const brands = ['Product A', 'Product B', 'Product C'];
    const text = keyword.getText();
    for (const i = 0; i < brands.length; i++) {
      if (text.indexOf(brand[i]) >= 0) {
        return true;
      }
    }
    return false;
  },
  labelName: 'Underperforming Branded'
}

Esta regla etiqueta todas las palabras clave con una CTR inferior al 2% y un CPC promedio superior a USD 1 durante los últimos 7 días si el texto de la palabra clave contiene, al menos, uno de los tres nombres de marca del producto.

Por lo general, solo te interesa etiquetar las palabras clave que están habilitadas y que residen en un grupo de anuncios y una campaña que sí están habilitados. En lugar de especificar estas condiciones en cada regla, la secuencia de comandos te permite proporcionar condiciones globales que cada regla usará:

GLOBAL_CONDITIONS: [
  'campaign.status = ENABLED',
  'ad_group.status = ENABLED',
  'ad_group_criterion.status = ENABLED'
]

El núcleo de la secuencia de comandos es una función que toma una regla, compila una KeywordSelector con las condiciones globales y específicas de la regla, y aplica el filtro a cada palabra clave resultante. Consulta la función getKeywordsForRule en la siguiente secuencia de comandos para ver la implementación completa.

La secuencia de comandos envía un correo electrónico con un vínculo a una hoja de cálculo que enumera todas las palabras clave que etiquetó. Luego, puedes acceder a la IU de Google Ads para ver si deseas modificar esas palabras clave, por ejemplo, detenerlas, quitarlas o modificar sus ofertas.

Programación

La programación de la secuencia de comandos para que se ejecute depende de tus objetivos de etiquetado y la frecuencia con la que lo haces. Por ejemplo, si etiquetas palabras clave con bajo rendimiento, es posible que desees programar la secuencia de comandos para que se ejecute de forma semanal o incluso diaria para que puedas identificar con rapidez las palabras clave con bajo rendimiento y solucionarlas. Por otro lado, si usas la secuencia de comandos para facilitar esfuerzos puntuales que involucren el etiquetado de palabras clave, no es necesario programar la secuencia de comandos para que se ejecute con regularidad.

Configuración

  • Crea una nueva secuencia de comandos de Google Ads con el código fuente que aparece a continuación. Usa una copia de esta hoja de cálculo de plantilla.
  • Actualiza el array RULES para expresar tus reglas de etiquetado. El código fuente que aparece a continuación muestra un ejemplo para etiquetar las palabras clave con bajo rendimiento asociadas con tu marca, así como las palabras clave genéricas con bajo rendimiento.
  • Actualiza SPREADSHEET_URL y RECIPIENT_EMAILS en tu secuencia de comandos.

Código fuente

// Copyright 2015, Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @name Keyword Labeler
 *
 * @overview The Keyword Labeler script labels keywords based on rules that
 *     you define. For example, you can create a rule to label keywords that
 *     are underperforming. Later, you can filter for this label in Google Ads
 *     to decide whether to pause or remove those keywords. Rules don't have
 *     to be based solely on a keyword's performance. They can also be based
 *     on properties of a keyword such as its text or match type. For example,
 *     you could define "branded" keywords as those containing proper nouns
 *     associated with your brand, then label those keywords based on
 *     different performance thresholds versus "non-branded" keywords.
 *     Finally, the script sends an email linking to a spreadsheet when new
 *     keywords have been labeled. See
 *     https://developers.google.com/google-ads/scripts/docs/solutions/labels
 *     for more details.
 *
 * @author Google Ads Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 2.0
 *
 * @changelog
 * - version 2.0
 *   - Updated to use new Google Ads scripts features.
 * - version 1.1.2
 *   - Added validation for external spreadsheet setup.
 * - version 1.1.1
 *   - Improvements to time zone handling.
 * - version 1.1
 *   - Modified to allow generic rules and labeling.
 * - version 1.0
 *   - Released initial version.
 */

const CONFIG = {
  // URL of the spreadsheet template.
  // This should be a copy of https://goo.gl/opviBb.
  SPREADSHEET_URL: 'YOUR_SPREADSHEET_URL',

  // Array of addresses to be alerted via email if labels are applied.
  RECIPIENT_EMAILS: [
    'YOUR_EMAIL_HERE'
  ],

  // Selector conditions to apply for all rules.
  GLOBAL_CONDITIONS: [
    'campaign.status = ENABLED',
    'ad_group.status = ENABLED',
    'ad_group_criterion.status = ENABLED'
  ],

  // Default date range over which statistics fields are retrieved.
  // Used when fetching keywords if a rule doesn't specify a date range.
  DEFAULT_DATE_RANGE: 'LAST_7_DAYS'
};

/**
 * Defines the rules by which keywords will be labeled.
 * The labelName field is required. Other fields may be null.
 * @type {Array.<{
 *     conditions: Array.<string>,
 *     dateRange: string,
 *     filter: function(Object): boolean,
 *     labelName: string,
 *   }>
 * }
 */
const RULES = [
  {
    conditions: [
      'metrics.ctr < 0.02',
      'metrics.average_cpc > 1',
    ],
    filter: function(keyword) {
      const brands = ['Product A', 'Product B', 'Product C'];
      const text = keyword.getText();
      for (const brand of brands) {
        if(text.indexOf(brand) >= 0) {
          return true;
        }
      }
      return false;
    },
    labelName: 'Underperforming Branded'
  },

  {
    conditions: [
      'metrics.ctr < 0.01',
      'metrics.average_cpc > 2',
    ],
    labelName: 'Underperforming'
  }
];

function main() {
  validateEmailAddresses();
  const results = processAccount();
  processResults(results);
}

/**
 * Processes the rules on the current account.
 *
 * @return {Array.<Object>} An array of changes made, each having
 *     a customerId, campaign name, ad group name, label name,
 *     and keyword text that the label was applied to.
 */
function processAccount() {
  ensureAccountLabels();
  const changes = applyLabels();

  return changes;
}

/**
 * Processes the results of the script.
 *
 * @param {Array.<Object>} changes An array of changes made, each having
 *     a customerId, campaign name, ad group name, label name,
 *     and keyword text that the label was applied to.
 */
function processResults(changes) {
  if (changes.length > 0) {
    const spreadsheetUrl = saveToSpreadsheet(changes, CONFIG.RECIPIENT_EMAILS);
    sendEmail(spreadsheetUrl, CONFIG.RECIPIENT_EMAILS);
  } else {
    console.log('No labels were applied.');
  }
}

/**
 * Retrieves the names of all labels in the account.
 *
 * @return {Array.<string>} An array of label names.
 */
function getAccountLabelNames() {
  const labelNames = [];

  for (const label of AdsApp.labels()) {
    labelNames.push(label.getName());
  }

  return labelNames;
}

/**
 * Checks that the account has a label for each rule and
 * creates the rule's label if it does not already exist.
 * Throws an exception if a rule does not have a labelName.
 */
function ensureAccountLabels() {
  const labelNames = getAccountLabelNames();

  for (var i = 0; i < RULES.length; i++) {
    const labelName = RULES[i].labelName;

    if (!labelName) {
      throw `Missing labelName for rule #${i}`;
    }

    if (labelNames.indexOf(labelName) == -1) {
      AdsApp.createLabel(labelName);
      labelNames.push(labelName);
    }
  }
}

/**
 * Retrieves the keywords in an account satisfying a rule
 * and that do not already have the rule's label.
 *
 * @param {Object} rule An element of the RULES array.
 * @return {Array.<Object>} An array of keywords.
 */
function getKeywordsForRule(rule) {
  let selector = AdsApp.keywords();
  let includedLabel;

  // Add global conditions.
  for (const globalCondition of CONFIG.GLOBAL_CONDITIONS) {
    selector = selector.withCondition(globalCondition);
  }

  // Add selector conditions for this rule.
  if (rule.conditions) {
    for (const ruleCondition of rule.conditions) {
      selector = selector.withCondition(ruleCondition);
    }
  }

  if(rule.labelName){
    includedLabel = AdsApp.labels()
        .withCondition(`label.name = '${rule.labelName}'`)
        .get();
    if(includedLabel.hasNext()){
      includedLabel=includedLabel.next();
    }
  }

  // Exclude keywords that already have the label.
  selector.withCondition(`LabelNames CONTAINS_NONE ["${rule.labelName}"]`);

  // Add a date range.
  selector = selector.forDateRange(rule.dateRange || CONFIG.DEFAULT_DATE_RANGE);

  // Get the keywords.
  const iterator = selector.get();
  const keywords = [];

  // Check filter conditions for this rule.
  for (const keyword of iterator) {
    if (!rule.filter || rule.filter(keyword)) {
      keywords.push(keyword);
    }
  }

  return keywords;
}

/**
 * For each rule, determines the keywords matching the rule and which
 * need to have a label newly applied, and applies it.
 *
 * @return {Array.<Object>} An array of changes made, each having
 *     a customerId, campaign name, ad group name, label name,
 *     and keyword text that the label was applied to.
 */
function applyLabels() {
  const changes = [];
  const customerId = AdsApp.currentAccount().getCustomerId();

  for (const rule of RULES) {
    const keywords = getKeywordsForRule(rule);
    const labelName = rule.labelName;

    for (const keyword of keywords) {

      keyword.applyLabel(labelName);

      changes.push({
        customerId: customerId,
        campaignName: keyword.getCampaign().getName(),
        adGroupName: keyword.getAdGroup().getName(),
        labelName: labelName,
        keywordText: keyword.getText(),
      });
    }
  }

  return changes;
}

/**
 * Outputs a list of applied labels to a new spreadsheet and gives editor access
 * to a list of provided emails.
 *
 * @param {Array.<Object>} changes An array of changes made, each having
 *     a customerId, campaign name, ad group name, label name,
 *     and keyword text that the label was applied to.
 * @param {Array.<Object>} emails An array of email addresses.
 * @return {string} The URL of the spreadsheet.
 */
function saveToSpreadsheet(changes, emails) {
  const template = validateAndGetSpreadsheet(CONFIG.SPREADSHEET_URL);
  const spreadsheet = template.copy('Keyword Labels Applied');

  // Make sure the spreadsheet is using the account's timezone.
  spreadsheet.setSpreadsheetTimeZone(AdsApp.currentAccount().getTimeZone());

  console.log(`Saving changes to spreadsheet at ${spreadsheet.getUrl()}`);

  const headers = spreadsheet.getRangeByName('Headers');
  const outputRange = headers.offset(1, 0, changes.length);

  const outputValues = [];
  for (const change of changes) {
    outputValues.push([
      change.customerId,
      change.campaignName,
      change.adGroupName,
      change.keywordText,
      change.labelName
    ]);
  }
  outputRange.setValues(outputValues);

  spreadsheet.getRangeByName('RunDate').setValue(new Date());

  for (const email of emails) {
    spreadsheet.addEditor(email);
  }

  return spreadsheet.getUrl();
}

/**
 * Sends an email to a list of email addresses with a link to a spreadsheet.
 *
 * @param {string} spreadsheetUrl The URL of the spreadsheet.
 * @param {Array.<Object>} emails An array of email addresses.
 */
function sendEmail(spreadsheetUrl, emails) {
  MailApp.sendEmail(emails.join(','), 'Keywords Newly Labeled',
      `Keywords have been newly labeled in your` +
      `Google Ads account(s). See ` +
      `${spreadsheetUrl} for details.`);
}

/**
 * DO NOT EDIT ANYTHING BELOW THIS LINE.
 * Please modify your spreadsheet URL and email addresses at the top of the file
 * only.
 */

/**
 * Validates the provided spreadsheet URL and email address
 * to make sure that they're set up properly. Throws a descriptive error message
 * if validation fails.
 *
 * @param {string} spreadsheeturl The URL of the spreadsheet to open.
 * @return {Spreadsheet} The spreadsheet object itself, fetched from the URL.
 * @throws {Error} If the spreadsheet URL or email hasn't been set
 */
function validateAndGetSpreadsheet(spreadsheeturl) {
  if (spreadsheeturl == 'YOUR_SPREADSHEET_URL') {
    throw new Error('Please specify a valid Spreadsheet URL. You can find' +
        ' a link to a template in the associated guide for this script.');
  }
  return SpreadsheetApp.openByUrl(spreadsheeturl);
}

/**
 * Validates the provided email address to make sure it's not the default.
 * Throws a descriptive error message if validation fails.
 *
 * @throws {Error} If the list of email addresses is still the default
 */
function validateEmailAddresses() {
  if (CONFIG.RECIPIENT_EMAILS &&
      CONFIG.RECIPIENT_EMAILS[0] == 'YOUR_EMAIL_HERE') {
    throw new Error('Please specify a valid email address.');
  }
}