Gebotstests

Bei der Bestimmung des besten Gebots für Ihre Keywords kann es hilfreich sein, verschiedene Gebote auszuprobieren, um herauszufinden, mit welchem Gebot Sie Ihre Ziele am besten erreichen. Wir zeigen Ihnen hier, wie Sie Gebote systematisch anpassen können, um den optimalen Wert für Keywords zu finden.

Das Skript passt die Gebote Ihrer Keywords anhand verschiedener Faktoren an und dokumentiert das Ergebnis dieser Änderungen.

Funktionsweise

Mit Tabellen arbeiten

Dieses Skript verwendet eine Google-Tabelle sowohl zur Speicherung des aktuellen Status (z. B. Gebotsfaktoren und Startgebote) als auch der Katalogleistung. Für jeden Schritt des Tests werden Keyword-Gebot, Klickrate (CTR), Klicks und Impressionen aufgezeichnet.

Die Gebotsfaktoren werden bei jedem Durchlauf des Skripts angewendet. Dabei wird jedes Mal der nächste noch nicht verwendete Gebotsfaktor auf Ihre Keyword-Gebote angewendet. Beispiel: Bei einem Startgebot von 1,00 $ und Gebotsfaktoren von 0,8 und 1,2 erhalten Sie Gebote in Höhe von 0,80 $ und 1,20 $.

Gebote aktualisieren

Über die Funktion updateBids wird der Gebotsfaktor des aktuellen Durchlaufs auf alle Keywords der ausgewählten Kampagne angewendet:

var keywordIter = campaign.keywords().get();
while (keywordIter.hasNext()) {
  var keyword = keywordIter.next();
  var oldBid = startingBids[keyword.getText()];
  if (!oldBid) {
    // Bei fehlendem Startgebot wurde das Keyword nach Beginn des Tests hinzugefügt.
    oldBid = keyword.bidding().getCpc() || keyword.getAdGroup().bidding().getCpc();
    startingBids[keyword.getText()] = oldBid;
  }
  var newBid = oldBid * multiplier;
  keyword.bidding().setCpc(newBid);
}

Der Code oben nimmt den maximalen CPC des Keywords (oder den maximalen CPC der Anzeigengruppe, falls für das Keyword kein Gebot festgelegt wurde) und wendet den Gebotsfaktor darauf an. Außerdem erkennt der Code, ob ein Keyword zwischen den einzelnen Skriptdurchläufen hinzugefügt wurde, und speichert den aktuellen maximalen CPC zur weiteren Verwendung.

Berichte zur Gebotsleistung

Bei jedem Skriptdurchlauf, bei dem Gebotsanpassungen für mindestens einen Zeitraum (Woche, Tag usw.) angewendet wurden, wird die Funktion outputReport ausgeführt. Für den Abruf der Messwerte für die einzelnen Keywords wird der Zeitraum zwischen dem Datum auf dem Tab "Gebotsfaktoren" und dem aktuellen Datum verwendet. Dieser Zeitraum wird zusammen mit dem Keyword-Gebot für diesen Zeitraum zur späteren Analyse in einer eigenen Tabelle gespeichert.

// Create a new sheet to output keywords to.
var reportSheet = spreadsheet.insertSheet(start + ' - ' + end);
var campaign = getCampaign();

var rows = [['Keyword', 'Max CPC', 'Clicks', 'Impressions', 'Ctr']];
var keywordIter = campaign.keywords().get();
while (keywordIter.hasNext()) {
  var keyword = keywordIter.next();
  var stats = keyword.getStatsFor(start, end);
  rows.push([keyword.getText(), keyword.bidding().getCpc(), stats.getClicks(),
      stats.getImpressions(), stats.getCtr()]);
}

reportSheet.getRange(1, 1, rows.length, 5).setValues(rows);

Das Skript wird über jedes Keyword der Kampagne iteriert und es wird eine Zeile zu einem Array mit folgenden Daten hinzugefügt: Keyword, max. CPC, Klicks, Impressionen und CTR. Dieses Array wird dann in die neue Tabelle ausgegeben, die entsprechend dem Start- und Enddatum des Berichtszeitraums benannt ist.

Wenn Sie andere Leistungskennzahlen als diejenigen im Skript verwenden, können Sie die Abfrage entsprechend anpassen. Fügen Sie dazu einen weiteren Eintrag in der ersten Zeile (Überschriftenzeile) hinzu:

var rows = [['Keyword', 'Max CPC', 'Clicks', 'Impressions', 'Ctr']];

und modifizieren Sie das Skript, um diese Messdaten zu berücksichtigen:

rows.push([keyword.getText(), keyword.bidding().getCpc(), stats.getClicks(),
    stats.getImpressions(), stats.getCtr()]);

Planung

Wir empfehlen Ihnen, zum Testen des Skripts eine Kampagne auszuwählen und das Skript wöchentlich oder entsprechend Ihren Anforderungen zu planen. Das Skript erstellt für die geplanten Zeiträume jeweils einen Leistungsbericht des vorangegangenen Zeitraums und wendet einen neuen Gebotsfaktor an.

Die Funktion main() enthält eine Abfrage zur Steuerung der einzelnen Schritte des Gebotstests. Nach Abschluss des Gebotstests wird dies protokolliert und bei weiteren Skriptdurchläufen werden keine zusätzlichen Änderungen vorgenommen:

if (finishedReporting) {
  Logger.log('Script complete, all bid modifiers tested and reporting. ' +
    'Please remove this script\'s schedule.');
}

Testergebnisse analysieren

Das Skript hat ein Array mit Gebotsanpassungen auf Ihre Keywords angewendet und die Leistung dieser Werte bei allen Keywords protokolliert. Jetzt müssen Sie entscheiden, wie Sie diese Daten nutzen. Berücksichtigen Sie dabei, dass dieselben Gebotsanpassungen nicht bei allen Keywords zu denselben Leistungsverbesserungen führen.

Wir haben sowohl den maximalen CPC des Keywords als auch verschiedene Leistungsindikatoren protokolliert. Jetzt liegt es an Ihnen, diese zu priorisieren. Werten Sie jedes Keyword während der einzelnen Zeiträume aus und wählen Sie anhand der Leistungskennzahlen das beste Gebot für jedes Keyword aus.

Einrichtung

  • Erstellen Sie eine Kopie dieser Tabelle (Datei -> Kopie erstellen).
  • Notieren Sie sich die URL der Kopie.
  • Erstellen Sie ein neues AdWords-Skript mit dem unten stehenden Quellcode.
  • Passen Sie den Wert der Variablen SPREADSHEET_URL an die URL Ihrer Kopie der Tabelle an. Wählen Sie dann eine Kampagne aus, mit der Sie den Gebotstest durchführen möchten, und ändern Sie den Wert der Variablen CAMPAIGN_NAME entsprechend.

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 Bid Testing
 *
 * @overview The Bid Testing script allows you to try different levels of
 *     bidding for keywords in your advertiser account to determine what bids
 *     work best to achieve your goals.
 *     See https://developers.google.com/adwords/scripts/docs/solutions/bid-testing
 *     for more details.
 *
 * @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 1.0.3
 *
 * @changelog
 * - version 1.0.3
 *   - Replaced deprecated keyword.setMaxCpc() and keyword.getMaxCpc().
 * - version 1.0.2
 *   - Added validation for user settings.
 * - version 1.0.1
 *   - Improvements to time zone handling.
 * - version 1.0
 *   - Released initial version.
 */

var SPREADSHEET_URL = 'YOUR_SPREADSHEET_URL';
var CAMPAIGN_NAME = 'YOUR_CAMPAIGN_NAME';

function main() {
  validateCampaignName();
  Logger.log('Using spreadsheet - %s.', SPREADSHEET_URL);
  var spreadsheet = validateAndGetSpreadsheet(SPREADSHEET_URL);
  spreadsheet.setSpreadsheetTimeZone(AdWordsApp.currentAccount().getTimeZone());

  var multipliersSheet = spreadsheet.getSheetByName('Multipliers');

  var multipliers = multipliersSheet.getDataRange().getValues();
  // Find if we have a multiplier left to apply.
  var multiplierRow = 1;
  for (; multiplierRow < multipliers.length; multiplierRow++) {
    // if we haven't marked a multiplier as applied, use it.
    if (!multipliers[multiplierRow][1]) {
      break;
    }
  }

  var today = Utilities.formatDate(new Date(),
      AdWordsApp.currentAccount().getTimeZone(), 'yyyyMMdd');

  var shouldReport = multiplierRow > 1;
  var shouldIncreaseBids = multiplierRow < multipliers.length;
  var finishedReporting = multipliersSheet.getSheetProtection().isProtected();

  if (shouldReport && !finishedReporting) {
    // If we have at least one multiplier marked as applied,
    // let's record performance since the last time we ran.
    var lastRun = multipliers[multiplierRow - 1][1];
    if (lastRun == today) {
      Logger.log('Already ran today, skipping');
      return;
    }
    outputReport(spreadsheet, lastRun, today);

    if (!shouldIncreaseBids) {
      // We've reported one iteration after we finished bids, so mark the sheet
      // protected.
      var permissions = multipliersSheet.getSheetProtection();
      permissions.setProtected(true);
      multipliersSheet.setSheetProtection(permissions);
      Logger.log('View bid testing results here: ' + SPREADSHEET_URL);
    }
  }

  if (shouldIncreaseBids) {
    // If we have a multiplier left to apply, let's do so.
    updateBids(spreadsheet, multipliers[multiplierRow][0]);
    multipliers[multiplierRow][1] = today;
    // Mark multiplier as applied.
    multipliersSheet.getDataRange().setValues(multipliers);
  }

  if (finishedReporting) {
    Logger.log('Script complete, all bid modifiers tested and reporting. ' +
      'Please remove this script\'s schedule.');
  }
}

function updateBids(spreadsheet, multiplier) {
  Logger.log('Applying bid multiplier of ' + multiplier);

  var startingBids = getStartingBids(spreadsheet);
  if (!startingBids) {
    startingBids = recordStartingBids(spreadsheet);
  }
  var campaign = getCampaign();
  var keywordIter = campaign.keywords().get();
  while (keywordIter.hasNext()) {
    var keyword = keywordIter.next();
    var oldBid = startingBids[keyword.getText()];
    if (!oldBid) {
      // If we don't have a starting bid, keyword has been added since we
      // started testing.
      oldBid = keyword.bidding().getCpc() || keyword.getAdGroup().bidding().getCpc();
      startingBids[keyword.getText()] = oldBid;
    }
    var newBid = oldBid * multiplier;
    keyword.bidding().setCpc(newBid);
  }
  saveStartingBids(spreadsheet, startingBids);
}

function outputReport(spreadsheet, start, end) {
  Logger.log('Reporting on ' + start + ' -> ' + end);

  // Create a new sheet to output keywords to.
  var reportSheet = spreadsheet.insertSheet(start + ' - ' + end);
  var campaign = getCampaign();

  var rows = [['Keyword', 'Max CPC', 'Clicks', 'Impressions', 'Ctr']];
  var keywordIter = campaign.keywords().get();
  while (keywordIter.hasNext()) {
    var keyword = keywordIter.next();
    var stats = keyword.getStatsFor(start, end);
    rows.push([keyword.getText(), keyword.bidding().getCpc(), stats.getClicks(),
        stats.getImpressions(), stats.getCtr()]);
  }

  reportSheet.getRange(1, 1, rows.length, 5).setValues(rows);
}

function recordStartingBids(spreadsheet) {
  var startingBids = {};
  var keywords = getCampaign().keywords().get();
  while (keywords.hasNext()) {
    var keyword = keywords.next();
    var bid = keyword.bidding().getCpc() || keyword.getAdGroup().bidding().getCpc();
    startingBids[keyword.getText()] = bid;
  }
  saveStartingBids(spreadsheet, startingBids);
  return startingBids;
}

function getStartingBids(spreadsheet) {
  var sheet = spreadsheet.getSheetByName('Starting Bids');
  if (!sheet) {
    return;
  }
  var rawData = sheet.getDataRange().getValues();
  var startingBids = {};
  for (var i = 0; i < rawData.length; i++) {
    startingBids[rawData[i][0]] = rawData[i][1];
  }
  return startingBids;
}

function saveStartingBids(spreadsheet, startingBids) {
  var sheet = spreadsheet.getSheetByName('Starting Bids');
  if (!sheet) {
    sheet = spreadsheet.insertSheet('Starting Bids');
  }
  var rows = [];
  for (var keyword in startingBids) {
    rows.push([keyword, startingBids[keyword]]);
  }
  sheet.getRange(1, 1, rows.length, 2).setValues(rows);
}

function dateToString(date) {
  return date.getFullYear() + zeroPad(date.getMonth() + 1) +
      zeroPad(date.getDate());
}

function zeroPad(n) {
  if (n < 10) {
    return '0' + n;
  } else {
    return '' + n;
  }
}

function getCampaign() {
  return AdWordsApp.campaigns().withCondition("Name = '" +
      CAMPAIGN_NAME + "'").get().next();
}

/**
 * Validates the provided campaign name and throws a descriptive error
 * if the user has not changed the email from the default fake name.
 *
 * @throws {Error} If the name is the default fake name.
 */
function validateCampaignName(){
  if (CAMPAIGN_NAME == "YOUR_CAMPAIGN_NAME") {
    throw new Error('Please use a valid campaign name.');
  }
}

/**
 * Validates the provided spreadsheet URL
 * to make sure that it's 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 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);
}

Feedback geben zu...

AdWords scripts
AdWords scripts