Anzeigenanpassung

Mit aktuellen und dynamischen Werten im Anzeigentext lassen sich äußerst überzeugende Anzeigen erstellen. Werbetreibende, die mit günstigen Preisen werben möchten, profitieren von aktuellen Anzeigentexten. Durch das Löschen und erneute Hinzufügen einer Anzeige gehen sämtliche Verlaufsdaten verloren. Außerdem muss die Anzeige erst wieder freigegeben werden. Mit AdWords-Skripts mit Anzeigenanpassungen ist es möglich, Live-Daten abzurufen und eine Anzeige beispielsweise mit aktuellen Preisen oder anderen Variablen zu aktualisieren.

In diesem Skript werden Anzeigen mit Parametern für einen hypothetischen Blumenladen eingerichtet, dessen Inventardaten in Google-Tabellen gespeichert sind. Eine Anzeige wird so eingerichtet, dass bei der Suche nach einer bestimmten vorrätigen Blume die Anzeige so angepasst wird, dass diese Blume und ihr Preis zu sehen sind. Wenn nach einem allgemeineren Begriff wie "Blumen" gesucht wird, wird in der Anzeige die Blume dargestellt, von der am meisten vorrätig ist. So kann der Händler mehr davon verkaufen, solange sie noch frisch sind.

In echten Kampagnen verfügen Sie über eigene Datenquellen, die in Google-Tabellen, in Google Drive oder in anderen, über UrlFetchApp abrufbaren Datenfeeds gespeichert sind. Sie können dieses Skript so anpassen, dass es anstelle der Blumendaten Ihre eigenen Daten verwendet.

Mit Anzeigenanpassungen können Sie die mit Anzeigenparametern zur Verfügung gestellten Funktionen erweitern: - Beliebiger Text kann anstelle von Zahlen verwendet werden. - Anzeigenanpassungen können auf Kampagnen- oder Anzeigengruppenebene und nicht nur auf Keyword-Ebene angewendet werden. - Anzeigenanpassungen erfassen Statistiken, die Sie analysieren und in Berichte aufnehmen können.

Funktionsweise

Datenquellen, Elemente und Anzeigen

Angepasste Anzeigen sind grundsätzlich normale Textanzeigen. Allerdings werden darin Textteile durch benutzerdefinierte Daten ersetzt:

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();

In diese Textanzeige werden Werte eingefügt, die aus der Datenquelle "Flowers" stammen. Jede Datenquelle enthält einen Satz zugehöriger Anzeigenanpassungen und deklariert einen Satz "Attribute", die in Anzeigen eingesetzt werden können. In diesem Fall sind das "name" und "name_lowercase" mit Daten vom Typ text und "price" mit Daten vom Typ 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();

Außerdem enthält jede Quelle einzelne Anzeigenanpassungselemente, die die in die Anzeige einzufügenden tatsächlichen Werte enthalten und Statistiken für die jeweilige Anzeigenvariation erfassen. Anzeigenanpassungen können für das ganze Konto gelten oder auf bestimmte Kampagnen, Anzeigengruppen oder Keywords ausgerichtet werden. Das folgende Skript erstellt Anzeigenanpassungen für jede vorrätige Blume. Sie wird angezeigt, wenn nach dieser Blume gesucht wird:

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();

Welche Daten werden verwendet?

Dieses Skript liest den Blumenbestand in eine Google-Tabelle ein:

// 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;
}

Anpassungen aktualisieren

Für jede Blume in der Bestandstabelle versucht das Skript, das Preisattribut für die entsprechende Anzeigenanpassung zu aktualisieren. Wenn noch keine entsprechende Anpassung vorhanden ist, wird eine neue Anzeigenanpassung erstellt. Es merkt sich auch, welche Blume den höchsten Lagerbestand hat und ändert oder erstellt dann die Anzeigenanpassung "Default". Diese gilt für die gesamte Anzeigengruppe unabhängig davon, welches Keyword die Anzeige auslöst.

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();
  }
}

Einrichtung

  • Erstellen Sie eine neue Anzeigengruppe und fügen Sie alle gewünschten Keywords hinzu.
  • Erstellen Sie ein tabellenbasiertes Skript mit dem unten stehenden Quellcode. Verwenden Sie dazu eine Kopie der Tabellenvorlage.
  • Passen Sie die Werte von SPREADSHEET_URL, CAMPAIGN und ADGROUP im Code an.
  • Planen Sie das Skript für eine stündliche Ausführung.

Quellcode


// 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;
}

Feedback geben zu...

AdWords Scripts
AdWords Scripts