Personalizador de anúncios

A exibição de valores atualizados e dinâmicos no texto do anúncio pode ser algo muito atrativo para os usuários. Um anunciante que deseja promover seus preços se beneficia ao manter o texto do anúncio atualizado. Ao excluir e voltar a adicionar um anúncio, ele perde informações do histórico e causa um atraso nas verificações de política. O uso de scripts do Google AdWords com personalizadores de anúncios possibilita buscar dados em tempo real e atualizar um anúncio para refletir os preços atuais, os níveis do inventário ou qualquer outra variável.

Por exemplo, esse script configuraria anúncios parametrizados para uma floricultura hipotética cujos dados do inventário estivessem armazenados nas Planilhas do Google. Um anúncio seria configurado assim quando uma pessoa pesquisasse um tipo específico de flor. O anúncio seria personalizado para mostrar a flor e o preço. Quando alguém pesquisasse um termo mais genérico, como "flores", o anúncio mostraria a flor em maior quantidade em estoque no inventário. Assim, a loja poderia vender mais desse tipo enquanto as flores ainda estivessem frescas.

Em campanhas publicitárias reais, você terá suas próprias origens de dados, independentemente de elas serem armazenadas em Planilhas do Google ou em outros feeds de dados recuperáveis por meio de UrlFetchApp. Você pode modificar esse script para incorporar seus próprios dados a esse exemplo, em vez de usar dados de flores.

Os personalizadores de anúncios melhoram a funcionalidade fornecida pelos parâmetros de anúncio: - É possível usar um texto absoluto em vez de apenas números. - É possível aplicar personalizadores de anúncios nos níveis da campanha e do grupo de anúncios, não apenas no nível da palavra-chave. - Os personalizadores de anúncios coletam estatísticas que você pode analisar e inserir em relatórios.

Como funciona

Origens de dados, itens e anúncios

Os anúncios personalizados são basicamente anúncios de texto normais, exceto pelo fato de que parte do texto deles são substituídos por dados personalizados:

adGroup.newTextAdBuilder()
    .withHeadline('{=Flowers.name} For Sale')
    .withDescription1('Fresh cut {=Flowers.name_lowercase}')
    .withDescription2('starting at {=Flowers.price}')
    .withDisplayUrl('www.example.com')
    .withFinalUrl('http://www.example.com')
    .build();

Esse anúncio de texto incluirá dados que buscará usando a origem de dados chamada "Flowers". Cada origem de dados possui um conjunto de personalizadores de anúncios e define um conjunto de "atributos" que podem ser substituídos nos anúncios. Nesse caso, "name" e "name_lowercase", que têm dados do tipo text, e "price" que possui dados do tipo price:

AdWordsApp.newAdCustomizerSourceBuilder()
    .withName('Flowers')
    .addAttribute('name', 'text')
    .addAttribute('name_lowercase', 'text')
    .addAttribute('price', 'price')
    // Attributes named 'Custom ID' are special: the system will make sure
    // that all values in the data source have unique custom IDs.
    .addAttribute('Custom ID', 'text')
    .build();

Por fim, cada origem contém itens de personalizadores de anúncios individuais (que carregam os reais valores que são substituídos nos anúncios) e acompanha as estatísticas da variação do anúncio específica. Os personalizadores de anúncios podem ser aplicados na conta toda ou ser segmentados para campanhas, grupos de anúncio ou palavras-chave específicos. Este script cria personalizadores de anúncios para cada flor em estoque, que será exibida quando ela for pesquisada:

source.adCustomizerItemBuilder()
    .withAttributeValue('Custom ID', flower.name.toLowerCase())
    .withAttributeValue('name', flower.name),
    .withAttributeValue('name_lowercase', flower.name.toLowerCase()),
    .withAttributeValue('price', flower.price)
    .withTargetKeyword(flower.name)
    .build();

Quais dados usar?

Esse script lê o inventário de flores a partir de uma Planilha do Google.

// Read flower / quantity / price data from a spreadsheet. In a real
// advertising campaign, you would have your own data source to read from.
function readFlowers(url) {
  var flowersByName = {};
  var spreadsheet = SpreadsheetApp.openByUrl(url);
  var sheet = spreadsheet.getSheets()[0];
  var data = sheet.getRange(2, 1, sheet.getMaxRows() - 1, 3).getValues();
  for (var i = 0; i < data.length; i++) {
    if (data[i][0]) {
      var flower = {
        name: data[i][0],
        quantity: parseFloat(data[i][1]),
        price: data[i][2]
      };
      if (typeof flower.price != 'string') {
        // Spreadsheets will sometimes coerce "$4.99" into just the number
        // 4.99. In that case, add the dollar sign back.
        flower.price = '$' + flower.price.toFixed(2);
      }
      flowersByName[flower.name] = flower;
    }
  }
  return flowersByName;
}

Atualização dos personalizadores

Para cada flor presente na planilha do inventário, o script tentará atualizar o atributo de preço do personalizador de anúncio correspondente ou criará um personalizador de anúncio novo, caso ainda não exista um correspondente. Ele também acompanha qual flor está em maior quantidade em estoque e, então, modifica ou cria um personalizador de anúncio "Padrão" que se aplica a todo o grupo de anúncios, independentemente da palavra-chave que aciona o anúncio.

function main() {
  var adGroup = getAdGroup(CAMPAIGN, ADGROUP);
  var source = getOrCreateDataSource();
  maybeCreateAds(adGroup);

  // Get all customizer items in the 'Flowers' data source, and create a map
  // from item ID to item.
  var customizers = source.items().get();
  var customizersById = {};
  while (customizers.hasNext()) {
    var customizer = customizers.next();
    customizersById[customizer.getAttributeValue('Custom ID')] = customizer;
  }

  // For each flower in inventory, update the matching ad customizer item's
  // 'price' attribute. Also find the flower with the highest quantity in stock.
  var flowersInStock = readFlowers(SPREADSHEET_URL);
  var mostInStock;
  var highestStock = 0;
  for (var flowerName in flowersInStock) {
    var flower = flowersInStock[flowerName];
    if (flower.quantity > highestStock) {
      highestStock = flower.quantity;
      mostInStock = flower;
    }

    var customizer = customizersById[flower.name.toLowerCase()];
    if (customizer) {
      customizer.setAttributeValue('price', flower.price);
    } else {
      source.adCustomizerItemBuilder()
          .withAttributeValue('Custom ID', flower.name.toLowerCase())
          .withAttributeValue('name', flower.name)
          .withAttributeValue('name_lowercase', flower.name.toLowerCase())
          .withAttributeValue('price', flower.price)
          .withTargetKeyword(flower.name)
          .build();
    }
  }

  // Point the 'default' customizer item to the flower that has the largest
  // quantity in stock. The default customizer item has no target keyword, so
  // it'll be triggered for generic search terms like 'flowers'.
  var defaultCustomizer = customizersById['Default'];
  if (defaultCustomizer) {
    defaultCustomizer.setAttributeValues({
      name: mostInStock.name,
      name_lowercase: mostInStock.name.toLowerCase(),
      price: mostInStock.price
    });
  } else {
    source.adCustomizerItemBuilder()
        .withAttributeValue('Custom ID', 'Default')
        .withAttributeValue('name', mostInStock.name)
        .withAttributeValue('name_lowercase', mostInStock.name.toLowerCase())
        .withAttributeValue('price', mostInStock.price)
        .build();
  }
}

Configuração

  • Crie um novo grupo de anúncios e adicione qualquer palavra-chave que você quiser.
  • Configure um script com base em planilha usando o código-fonte abaixo. Use uma cópia da planilha modelo.
  • Não se esqueça de atualizar SPREADSHEET_URL, CAMPAIGN e ADGROUP no código.
  • Agende a execução do script por hora.

Código-fonte


// 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 Ad Customizer
 *
 * @overview The Ad Customizer script shows how to setup parameterized ads in
 *     your account. This script generates ads for a hypothetical flower shop
 *     whose inventory data is stored in Google Spreadsheets. In real
 *     advertising campaigns, you'll need to substitute it with your own
 *     datasources. See
 *     https://developers.google.com/adwords/scripts/docs/solutions/customizer
 *     for more details.
 *
 * @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 1.0.2
 *
 * @changelog
 * - version 1.0.2
 *   - Added validation for external spreadsheet setup.
 * - version 1.0.1
 *   - Updated to use expanded text ads.
 * - version 1.0
 *   - Released initial version.
 */

// Replace this with the URL of the spreadsheet containing your data.
var SPREADSHEET_URL = 'YOUR_SPREADSHEET_URL';
// Replace this with the name of the campaign you want to use.
var CAMPAIGN = 'Campaign name';
// Replace this with the name of the ad group you want to use.
var ADGROUP = 'Flowers';

/**
 * Retrieves an ad group given a campaign and ad group name.
 *
 * @param {string} campaignName The campaign name.
 * @param {string} adGroupName The ad group name.
 * @return {!AdGroup} The ad group.
 * @throws if the specified campaign and ad group does not exist.
 */
function getAdGroup(campaignName, adGroupName) {
  return AdWordsApp.adGroups()
      .withCondition('Name = "' + adGroupName + '"')
      .withCondition('CampaignName = "' + campaignName + '"')
      .get()
      .next();
}

/**
 * Creates customized ads if they haven't already been created.
 *
 * @param {!AdGroup} adGroup The ad group within which to create the ads.
 */
function maybeCreateAds(adGroup) {
  var ads = adGroup.ads().get();
  while (ads.hasNext()) {
    var ad = ads.next();
    if (ad.isType().expandedTextAd()) {
      var expandedTextAd = ad.asType().expandedTextAd();
      if (expandedTextAd.getHeadlinePart1() == 'Flowers For Sale') {
        // The ads have already been created; no need to do more
        return;
      }
    }
  }

  // Reference the 'Flowers' data source here; text will be inserted when the
  // ad is served.
  adGroup.newAd().expandedTextAdBuilder()
      .withHeadlinePart1('{=Flowers.name} For Sale')
      .withHeadlinePart2('Fresh cut {=Flowers.name_lowercase}')
      .withDescription('starting at {=Flowers.price}')
      .withFinalUrl('http://example.com')
      .build();
  // All ad groups also need to have an ad without ad customizers to fall back
  // on, in case no ad customizers are able to serve.
  adGroup.newAd().expandedTextAdBuilder()
      .withHeadlinePart1('Flowers For Sale')
      .withHeadlinePart2('Fresh cut flowers')
      .withDescription('delivered for cheap')
      .withFinalUrl('http://www.example.com')
      .build();
}

/**
 * Retrieves or creates the Flowers data source.
 *
 * @return {!AdCustomizerSource}
 */
function getOrCreateDataSource() {
  var sources = AdWordsApp.adCustomizerSources().get();
  while (sources.hasNext()) {
    var source = sources.next();
    if (source.getName() == 'Flowers') {
      return source;
    }
  }
  return AdWordsApp.newAdCustomizerSourceBuilder()
      .withName('Flowers')
      .addAttribute('name', 'text')
      .addAttribute('name_lowercase', 'text')
      .addAttribute('price', 'price')
      // Attributes named 'Custom ID' are special: the system will make sure
      // that all values in the data source have unique custom IDs.
      .addAttribute('Custom ID', 'text')
      .build()
      .getResult();
}

/**
 * Reads flower / quantity / price data from a spreadsheet. In a real
 * advertising campaign, you would have your own data source to read from.
 *
 * @param {string} url The URL of the spreadsheet to read from.
 * @return {!Object} A lookup of flower details, by name.
 */
function readFlowers(url) {
  var flowersByName = {};

  Logger.log('Using spreadsheet - %s.', url);
  var spreadsheet = validateAndGetSpreadsheet(url);

  var sheet = spreadsheet.getSheets()[0];
  var data = sheet.getRange(2, 1, sheet.getMaxRows() - 1, 3).getValues();
  for (var i = 0; i < data.length; i++) {
    if (data[i][0]) {
      var flower = {
        name: data[i][0],
        quantity: parseFloat(data[i][1]),
        price: data[i][2]
      };
      if (typeof flower.price != 'string') {
        // Spreadsheets will sometimes coerce "$4.99" into just the number 4.99.
        // In that case, add the dollar sign back.
        flower.price = '$' + flower.price.toFixed(2);
      }
      flowersByName[flower.name] = flower;
    }
  }
  return flowersByName;
}

/**
 * Obtains a mapping from Customizer ID to Customizer object.
 *
 * @param {!AdCustomizerSource} source
 * @return {!Object.<!AdCustomizerItem>} A mapping from custom ID to item.
 */
function getCustomizersById(source) {
  var customizers = source.items().get();
  var customizersById = {};
  while (customizers.hasNext()) {
    var customizer = customizers.next();
    customizersById[customizer.getAttributeValue('Custom ID')] = customizer;
  }
  return customizersById;
}

/**
 * Sets the price value of existing customizers, or creates new customizers
 * where none exists.
 *
 * @param {!AdCustomizerSource} source
 * @param {!Object} flowersInStock A mapping of flower names to objects
 *     objects containing flower details, including price.
 * @param {!Object.<!AdCustomizerItem>} customizersById A mapping from custom ID
 *     to ad customizer.
 * @return {!Object} The object representing the flower with most in stock.
 */
function setCustomizerValues(source, flowersInStock, customizersById) {
  var mostInStock;
  var highestStock = 0;
  for (var flowerName in flowersInStock) {
    var flower = flowersInStock[flowerName];
    if (flower.quantity > highestStock) {
      highestStock = flower.quantity;
      mostInStock = flower;
    }
    var customizer = customizersById[flower.name.toLowerCase()];
    if (customizer) {
      customizer.setAttributeValue('price', flower.price);
    } else {
      source.adCustomizerItemBuilder()
          .withAttributeValue('Custom ID', flower.name.toLowerCase())
          .withAttributeValue('name', flower.name)
          .withAttributeValue('name_lowercase', flower.name.toLowerCase())
          .withAttributeValue('price', flower.price)
          .withTargetKeyword(flower.name)
          .build();
    }
  }
  return mostInStock;
}

/**
 * Main entry point.
 */
function main() {
  var adGroup = getAdGroup(CAMPAIGN, ADGROUP);
  var source = getOrCreateDataSource();
  maybeCreateAds(adGroup);

  // Get all customizer items in the 'Flowers' data source, and create a map
  // from item ID to item.
  var customizersById = getCustomizersById(source);

  // For each flower in inventory, update the matching ad customizer item's
  // 'price' attribute. Also find the flower with the highest quantity in stock.
  var flowersInStock = readFlowers(SPREADSHEET_URL);
  var mostInStock =
      setCustomizerValues(source, flowersInStock, customizersById);

  // Point the 'default' customizer item to the flower that has the largest
  // quantity in stock. The default customizer item has no target keyword, so
  // it'll be triggered for generic search terms like 'flowers'.
  var defaultCustomizer = customizersById['Default'];
  if (defaultCustomizer) {
    defaultCustomizer.setAttributeValues({
      name: mostInStock.name,
      name_lowercase: mostInStock.name.toLowerCase(),
      price: mostInStock.price
    });
  } else {
    source.adCustomizerItemBuilder()
        .withAttributeValue('Custom ID', 'Default')
        .withAttributeValue('name', mostInStock.name)
        .withAttributeValue('name_lowercase', mostInStock.name.toLowerCase())
        .withAttributeValue('price', mostInStock.price)
        .build();
  }
}

/**
 * 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.');
  }
  var spreadsheet = SpreadsheetApp.openByUrl(spreadsheeturl);
  return spreadsheet;
}

Enviar comentários sobre…

Precisa de ajuda? Acesse nossa página de suporte.