Tester les enchères

Icône Enchères

Lorsque vous définissez la meilleure enchère pour vos mots clés, il peut être efficace d'essayer différents niveaux d'enchères afin de déterminer celles qui sont les plus efficaces pour atteindre vos objectifs. Dans cet article, nous vous expliquons comment ajuster systématiquement vos enchères afin de déterminer l'enchère idéale pour vos enchères de mots clés.

Ce script ajuste les enchères de vos mots clés en fonction d'une série de multiplicateurs et enregistre les résultats de chaque modification.

Fonctionnement

Ce script utilise une feuille de calcul Google pour stocker l'état (par exemple, les multiplicateurs d'enchères et les enchères de départ) et les performances du catalogue (pour chaque intervalle de test des enchères, il enregistre l'enchère de mot clé, le CTR, les clics et les impressions).

Les multiplicateurs d'enchères sont ensuite appliqués les uns après les autres à chaque itération du script. À chaque exécution, le prochain multiplicateur d'enchères inutilisé est appliqué à vos enchères de mots clés. Par exemple, une enchère de départ de 1 € et des multiplicateurs de 0,8 et 1,2 généreront des enchères de 0,80 € et 1,20 €.

Mises à jour des enchères

La fonction updateBids() applique le multiplicateur de cette itération à tous les mots clés de la campagne choisie:

const keywordIter = campaign.keywords().get();
for (const keyword of keywordIter) {
  let 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;
  }
  const newBid = oldBid * multiplier;
  keyword.bidding().setCpc(newBid);
}

Ce code utilise l'enchère au CPC max. du mot clé (ou l'enchère au CPC max. par défaut du groupe d'annonces, si le mot clé n'est associé à aucune enchère) et applique le multiplicateur. Il détecte également si un mot clé a été ajouté entre les exécutions du script et stocke l'enchère au CPC max. actuelle pour référence ultérieure.

Rapports sur les performances des enchères

Chaque fois que le script est exécuté et que des enchères sont appliquées pour au moins une période (semaine, jour, etc.), la fonction outputReport est exécutée. La date indiquée dans l'onglet "Multiplicateurs" et la date du jour sont utilisées comme plage de dates pour obtenir des métriques pour chaque mot clé. Les métriques ainsi que l'enchère de mot clé pour la période définie par la plage de dates sont stockées dans une feuille distincte en vue d'une analyse ultérieure.

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

const rows = [['Keyword', 'Max CPC', 'Clicks', 'Impressions', 'Ctr']];
const keywordIter = campaign.keywords().get();
for (const keyword of keywordIter) {
  const 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);

Chaque mot clé de la campagne fait l'objet d'une itération. De nouvelles lignes sont ajoutées et contiennent les tableaux suivants : "Mot clé", "CPC max.", "Clics", "Impressions" et "CTR". Ce tableau est ensuite écrit dans la nouvelle feuille (nommée en fonction des dates de début et de fin du rapport).

Si vous utilisez d'autres indicateurs clés de performance que ceux du script, vous pouvez modifier cette logique pour les inclure. Pour ce faire, ajoutez une autre entrée dans la première ligne (ligne d'en-tête) comme ceci:

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

Modifiez le script pour inclure également cette métrique:

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

Planification

Nous vous recommandons de choisir une campagne pour tester le script et de planifier son exécution toutes les semaines (ou selon le calendrier qui vous convient). À l'intervalle planifié, le script génère un rapport sur les performances de la période précédente (le cas échéant) et applique un nouveau multiplicateur d'enchères.

La fonction "main" contient la logique qui régit chaque étape de vos tests d'enchères. Une fois les tests d'enchères terminés, le script consigne ce fait. Aucune modification supplémentaire ne sera apportée aux exécutions futures:

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

Résultats du test

Une fois que le script a appliqué un tableau de modificateurs d'enchères à vos mots clés et consigné les performances de ces valeurs pour l'ensemble des mots clés, vous pouvez déterminer comment exploiter ces données. Gardez à l'esprit que le même modificateur d'enchères peut ne pas produire les mêmes avantages en termes de performances pour tous les mots clés.

Pour déterminer les métriques les plus importantes, l'enchère au CPC max. du mot clé ainsi que divers indicateurs de performances sont à votre disposition. Vous pouvez ensuite évaluer chaque mot clé à plusieurs intervalles et déterminer la meilleure enchère possible pour chacun d'eux selon votre propre jugement.

Prérequis

  • Créez une copie de cette feuille de calcul.
  • Notez l'URL de la copie.
  • Créez un script Google Ads avec le code source ci-dessous.
  • Remplacez la valeur de la variable SPREADSHEET_URL par l'URL de votre copie de la feuille de calcul.
  • Sélectionnez la campagne sur laquelle effectuer vos tests d'enchères et modifiez la valeur de la variable CAMPAIGN_NAME pour qu'elle corresponde à celle-ci.

Code source

// 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/google-ads/scripts/docs/solutions/bid-testing
 *     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.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.
 */

const SPREADSHEET_URL = 'YOUR_SPREADSHEET_URL';
const CAMPAIGN_NAME = 'YOUR_CAMPAIGN_NAME';

const FIELDS = ['ad_group_criterion.keyword.text',
                'ad_group.cpc_bid_micros',
                'metrics.impressions',
                'metrics.clicks',
                'metrics.ctr',
                'campaign.id'];

function main() {
  validateCampaignName();
  console.log(`Using spreadsheet - ${SPREADSHEET_URL}`);
  const spreadsheet = validateAndGetSpreadsheet(SPREADSHEET_URL);
  spreadsheet.setSpreadsheetTimeZone(AdsApp.currentAccount().getTimeZone());

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

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

  const today = Utilities.formatDate(new Date(),
      AdsApp.currentAccount().getTimeZone(), 'yyyyMMdd');

  let shouldReport = multiplierRow > 1;
  let shouldIncreaseBids = multiplierRow < multipliers.length;
  const 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.
    const lastRun = multipliers[multiplierRow - 1][1];
    if (lastRun == today) {
      console.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.
      const permissions = multipliersSheet.getSheetProtection();
      permissions.setProtected(true);
      multipliersSheet.setSheetProtection(permissions);
      console.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) {
    console.log(`Script complete, all bid modifiers tested and reporting. ` +
      `Please remove this script's schedule.`);
  }
}

function updateBids(spreadsheet, multiplier) {
  console.log(`Applying bid multiplier of ${multiplier}`);

  let startingBids = getStartingBids(spreadsheet);
  if (!startingBids) {
    startingBids = recordStartingBids(spreadsheet);
  }
  const campaign = getCampaign();
  const keywordIter = campaign.keywords().get();
  for (const keyword of keywordIter) {
    let 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;
    }
    const newBid = oldBid * multiplier;
    keyword.bidding().setCpc(newBid);
  }
  saveStartingBids(spreadsheet, startingBids);
}

function outputReport(spreadsheet, start, end) {
  console.log(`Reporting on ${start} -> ${end}`);

  // Create a new sheet to output keywords to.
  const reportSheet = spreadsheet.insertSheet(`${start} - ${end}`);

  const rows = [['Keyword', 'Max CPC', 'Clicks', 'Impressions', 'Ctr']];
  const fields = FIELDS.join(",");
  const keywordIter =
        AdsApp.search(`SELECT ${fields} FROM keyword_view ` +
                      `WHERE campaign.name = '${CAMPAIGN_NAME}' ` +
                      `AND segments.date BETWEEN '${start}' AND '${end}'`);
  for (const keyword of keywordIter) {
    if (keyword.metrics.impressions == 0)
      keyword.metrics.ctr = 0;
    rows.push([keyword.adGroupCriterion.keyword.text,
               keyword.adGroup.cpcBidMicros/1000000,
               keyword.metrics.clicks,
               keyword.metrics.impressions,
               parseFloat(keyword.metrics.ctr).toFixed(5)]);
  }

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

function recordStartingBids(spreadsheet) {
  const startingBids = {};
  const keywords = getCampaign().keywords().get();
  for (const keyword of keywords) {
    const bid = keyword.bidding().getCpc() ||
          keyword.getAdGroup().bidding().getCpc();
    startingBids[keyword.getText()] = bid;
  }
  saveStartingBids(spreadsheet, startingBids);
  return startingBids;
}

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

function saveStartingBids(spreadsheet, startingBids) {
  let sheet = spreadsheet.getSheetByName('Starting Bids');
  if (!sheet) {
    sheet = spreadsheet.insertSheet('Starting Bids');
  }
  const rows = [];
  for (const 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 AdsApp.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);
}