Модификатор объявлений

Объявления, содержащие динамически изменяющиеся значения, более информативны по сравнению с обычными и всегда содержат актуальные сведения. Стандартный подход, при котором объявления удаляются и добавляются снова, сопряжен с потерей статистических данных и затратами времени на проверку соответствия правилам. Скрипты AdWords с модификаторами объявлений позволяют извлекать и обновлять данные динамически по мере изменения текущих цен или любых других переменных.

Этот скрипт настраивает объявления для цветочного магазина, данные о товарах которого хранятся в электронной таблице Google. Объявления настроены таким образом, что при поиске по названию цветов, которые есть в продаже, показывается соответствующая реклама с указанием цены. Если же пользователь ищет просто "цветы", показывается реклама цветов, которых больше всего на складе, чтобы распродать их, пока они свежие.

Чтобы использовать собственные данные, считываемые из электронной таблицы Google или с помощью функции UrlFetchApp, в реальных кампаниях внесите соответствующие изменения в код скрипта.

Модификаторы объявлений дополняют функции параметров. С их помощью можно динамически менять не только цифры, но и текст. Учтите, что модификаторы можно применять на уровне кампании, группы объявлений или ключевых слов. Кроме того, при их использовании собирается статистика, доступная для анализа в виде отчетов.

Принцип работы

Источники данных, товары и объявления

Объявления, подобранные по интересам – это, как правило, обычные текстовые объявления. Но текст в них меняется динамически:

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

В это текстовое объявление будут подставляться значения из источника данных под названием Flowers ("Цветы"). В каждом источнике данных содержится один набор модификаторов объявления, а также набор атрибутов – в данном случае name и name_lowercase (данные типа text), а также price (данные типа 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();

Наконец, каждый источник содержит модификаторы для отдельного объявления, которые представляют собой конкретные значения для подстановки. При этом отслеживается статистика для каждого варианта. Применить модификаторы можно на уровне ключевых слов, групп объявлений или кампаний. Данный скрипт создает модификаторы для каждого вида цветов в продаже. Соответствующие объявления отображаются при поиске по названию определенного цветка:

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

Выбор данных

Этот скрипт считывает ресурс для определенного вида цветов из электронной таблицы 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;
}

Обновление модификаторов

Для каждого вида цветов в таблице скрипт будет пытаться обновить атрибут цены для соответствующего модификатора. Если модификатора нет, система создаст его. Также скрипт отслеживает, каких цветов больше всего в продаже, а затем меняет модификатор по умолчанию, который действует на уровне всей группы объявлений независимо от ключевых слов.

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

Настройка

  • Создайте новую группу объявлений и добавьте в нее ключевые слова.
  • Создайте скрипт на основе электронной таблицы, используя приведенный ниже исходный код. Используйте копию шаблона таблицы.
  • Не забудьте обновить элементы SPREADSHEET_URL, CAMPAIGN, и ADGROUP в коде.
  • Запланируйте ежечасное выполнение скрипта.

Код


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

Оставить отзыв о...

Текущей странице
Скрипты AdWords
Скрипты AdWords