Vários licitantes: conta de administrador

This is a Manager Account script. For operating on a single account, use the Single Account version of the script.

O script Vários licitantes da MCC estende vários licitantes para funcionamento com várias contas em uma conta da MCC.

O script Vários licitantes da MCC oferece funcionalidades semelhantes àquelas das Regras automatizadas com base em uma planilha. Cada linha de uma planilha está em vigor da mesma forma que uma Regra automatizada inteira para uma conta específica. O gerenciamento de centenas de regras em várias contas secundárias em uma MCC (uma tarefa difícil no Google AdWords) torna-se muito mais fácil.

A planilha acima demonstra uma única regra que:

  • Procura estatísticas de LAST_WEEK para o ID de cliente: 918-501-8835.
  • Encontra todas as palavras-chave na campanha para dispositivos móveis que receberam menos de cinco cliques e cuja CTR seja maior do que 25%.
  • Aumenta seus lances em 10%, sem exceder US$ 1,40.

Como funciona

Esse script funciona da mesma forma que o script "vários licitantes". A única funcionalidade adicional é que a coluna de ID do cliente deve ser a primeira coluna da planilha, além de conter os IDs de cliente aos quais você deseja aplicar a regra automatizada. Essa deve ser uma conta de anunciante, não uma conta da MCC. É possível usar apenas um ID de cliente por linha. Sendo assim, crie várias linhas se você deseja que a mesma regra seja aplicada a várias contas.

Agendamento

As opções de agendamento mais usadas para regras de lances são "Diariamente" ou "Semanalmente". Saiba como a frequência do agendamento interagirá com o período de estatísticas selecionado por você. Como as estatísticas do Google AdWords podem atrasar até três horas, evite agendar seu script "A cada hora".

Em alguns casos, pode ser que agendar o script não faça diferença nenhuma. Aplique apenas o que gerar resultados nas contas que você gerencia.

Configuração

  • Configure um script com base em planilha usando o código-fonte abaixo. Use a planilha modelo de vários licitantes da MCC.
  • Lembre-se de atualizar YOUR_SPREADSHEET_URL no texto do seu exemplo de código.
  • Agende o script para ser executado "Diariamente" se necessário.

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 MCC Multi Bidder
 *
 * @overview The MCC Multi Bidder script offers functionality similar to that of
 *     Automated Rules based on a spreadsheet. The script runs for multiple
 *     accounts under an MCC account. See
 *     https://developers.google.com/adwords/scripts/docs/solutions/mccapp-multi-bidder
 *     for more details.
 *
 * @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 1.0.3
 *
 * @changelog
 * - version 1.0.3
 *   - Replaced deprecated keyword.getMaxCpc() and keyword.setMaxCpc().
 * - version 1.0.2
 *   - Added validation of user settings.
 * - version 1.0.1
 *   - Improvements to time zone handling.
 * - version 1.0
 *   - Released initial version.
 */

// The spreadsheet URL. This should be a copy of https://goo.gl/6Lx1Be
var SPREADSHEET_URL = 'YOUR_SPREADSHEET_URL';

function main() {
  var mccAccount = AdWordsApp.currentAccount();

  var spreadsheetAccess = new SpreadsheetAccess(SPREADSHEET_URL, 'Rules');

  // Make sure the spreadsheet is using the account's timezone.
  spreadsheetAccess.spreadsheet.setSpreadsheetTimeZone(
      AdWordsApp.currentAccount().getTimeZone());
  prepareSheet(spreadsheetAccess);

  var row = spreadsheetAccess.nextRow();

  while (row != null) {
    var argument;
    var stopLimit;
    try {
      argument = parseArgument(spreadsheetAccess, row);
      stopLimit = parseStopLimit(spreadsheetAccess, row);
    } catch (e) {
      logError(spreadsheetAccess, e);
      row = spreadsheetAccess.nextRow();
      continue;
    }
    var customerId = row[spreadsheetAccess.CUSTOMERID_INDEX];
    var account = null;
    try {
      var accountIterator = MccApp.accounts().withIds([customerId]).get();
      if (accountIterator.totalNumEntities() == 0) {
        throw ('Missing account: ' + customerId);
      } else {
        account = accountIterator.next();
      }
    } catch (e) {
      logError(spreadsheetAccess, e);
      row = spreadsheetAccess.nextRow();
      continue;
    }

    MccApp.select(account);

    var selector = buildSelector(spreadsheetAccess, row);
    var keywords = selector.get();

    try {
      keywords.hasNext();
    } catch (e) {
      logError(e);
      row = spreadsheetAccess.nextRow();
      continue;
    }

    var action = row[spreadsheetAccess.RULE_INDEX];
    var results = applyRules(keywords, action, argument, stopLimit);
    logResult(spreadsheetAccess, 'Fetched ' + results.fetched + '\nChanged ' +
        results.changed);
    row = spreadsheetAccess.nextRow();
  }

  spreadsheetAccess.spreadsheet.getRangeByName('last_execution')
      .setValue(new Date());
}

/**
 * Prepares the spreadsheet for saving data.
 *
 * @param {Object} spreadsheetAccess the SpreadsheetAccess instance that
 *    handles the spreadsheet.
 */
function prepareSheet(spreadsheetAccess) {
  // Clear the results column.
  spreadsheetAccess.sheet.getRange(
    spreadsheetAccess.START_ROW,
    spreadsheetAccess.RESULTS_COLUMN_INDEX + spreadsheetAccess.START_COLUMN,
    spreadsheetAccess.MAX_COLUMNS, 1).clear();
}

/**
 * Builds a keyword selector based on the conditional column headers in the
 * spreadsheet.
 *
 * @param {Object} spreadsheetAccess the SpreadsheetAccess instance that
 *    handles the spreadsheet.
 * @param {Object} row the spreadsheet header row.
 * @return {Object} the keyword selector, based on spreadsheet header settings.
 */
function buildSelector(spreadsheetAccess, row) {
  var columns = spreadsheetAccess.getColumnHeaders();
  var selector = AdWordsApp.keywords();

  for (var i = spreadsheetAccess.FIRST_CONDITIONAL_COLUMN;
      i < spreadsheetAccess.RESULTS_COLUMN_INDEX; i++) {
    var header = columns[i];
    var value = row[i];
    if (!isNaN(parseFloat(value)) || value.length > 0) {
      if (header.indexOf("'") > 0) {
        value = value.replace(/\'/g, "\\'");
      } else if (header.indexOf('\"') > 0) {
        value = value.replace(/"/g, '\\\"');
      }
      var condition = header.replace('?', value);
      selector.withCondition(condition);
    }
  }
  selector.forDateRange(spreadsheetAccess.spreadsheet
      .getRangeByName('date_range').getValue());
  return selector;
}

/**
 * Applies the rules in the spreadsheet.
 *
 * @param {Object} keywords the keywords selector.
 * @param {String} action the action to be taken.
 * @param {String} argument the parameters for the operation specified by
 *   action.
 * @param {Number} stopLimit the upper limit to the bid value when applying
 *   rules.
 * @return {Object} the number of keywords that were fetched and modified.
 */
function applyRules(keywords, action, argument, stopLimit) {
  var fetched = 0;
  var changed = 0;

  while (keywords.hasNext()) {
    var keyword = keywords.next();
    var oldBid = keyword.bidding().getCpc();
    var newBid = 0;
    fetched++;
    if (action == 'Add') {
      newBid = addToBid(oldBid, argument, stopLimit);
    } else if (action == 'Multiply by') {
      newBid = multiplyBid(oldBid, argument, stopLimit);
    } else if (action == 'Set to First Page Cpc' ||
          action == 'Set to Top of Page Cpc') {
      var newBid = action == 'Set to First Page Cpc' ?
         keyword.getFirstPageCpc() : keyword.getTopOfPageCpc();
      var isPositive = newBid > oldBid;
      newBid = applyStopLimit(newBid, stopLimit, isPositive);
    }
    if (newBid < 0) {
      newBid = 0.01;
    }
    newBid = newBid.toFixed(2);
    if (newBid != oldBid) {
      changed++;
    }
    keyword.bidding().setCpc(newBid);
  }
  return {
    'fetched': fetched,
    'changed': changed
  };
}

/**
 * Adds a value to an existing bid, while applying a stop limit.
 *
 * @param {Number} oldBid the existing bid.
 * @param {Number} argument the bid increment to apply.
 * @param {Number} stopLimit the cutoff limit for modified bid.
 * @return {Number} the modified bid.
 */
function addToBid(oldBid, argument, stopLimit) {
  return applyStopLimit(oldBid + argument, stopLimit, argument > 0);
}

/**
 * Multiplies an existing bid by a value, while applying a stop limit.
 *
 * @param {Number} oldBid the existing bid.
 * @param {Number} argument the bid multiplier.
 * @param {Number} stopLimit the cutoff limit for modified bid.
 * @return {Number} the modified bid.
 */
function multiplyBid(oldBid, argument, stopLimit) {
  return applyStopLimit(oldBid * argument, stopLimit, argument > 1);
}

/**
 * Applies a cutoff limit to a bid modification.
 *
 * @param {Number} newBid the modified bid.
 * @param {Number} stopLimit the bid cutoff limit.
 * @param {Boolean} isPositive true, if the stopLimit is an upper cutoff limit,
 *    false if it a lower cutoff limit.
 * @return {Number} the modified bid, after applying the stop limit.
 */
function applyStopLimit(newBid, stopLimit, isPositive) {
  if (stopLimit) {
    if (isPositive && newBid > stopLimit) {
      newBid = stopLimit;
    } else if (!isPositive && newBid < stopLimit) {
      newBid = stopLimit;
    }
  }
  return newBid;
}

/**
 * Parses the argument for an action on the spreadsheet.
 *
 * @param {Object} spreadsheetAccess the SpreadsheetAccess instance that
 *    handles the spreadsheet.
 * @param {Object} row the spreadsheet action row.
 * @return {Number} the parsed argument for the action.
 * @throws error if argument is missing, or is not a number.
 */
function parseArgument(spreadsheetAccess, row) {
  if (row[spreadsheetAccess.ARGUMENT_INDEX].length == 0 &&
      (row[spreadsheetAccess.RULE_INDEX] == 'Add' ||
          row[spreadsheetAccess.RULE_INDEX] == 'Multiply by')) {
    throw ('\"Argument\" must be specified.');
  }
  var argument = parseFloat(row[spreadsheetAccess.ARGUMENT_INDEX]);
  if (isNaN(argument)) {
    throw 'Bad Argument: must be a number.';
  }
  return argument;
}

/**
 * Parses the stop limit for an action on the spreadsheet.
 *
 * @param {Object} spreadsheetAccess the SpreadsheetAccess instance that
 *    handles the spreadsheet.
 * @param {Object} row the spreadsheet action row.
 * @return {Number} the parsed stop limit for the action.
 * @throws error if the stop limit is not a number.
 */
function parseStopLimit(spreadsheetAccess, row) {
  if (row[spreadsheetAccess.STOP_LIMIT_INDEX].length == 0) {
    return null;
  }
  var limit = parseFloat(row[spreadsheetAccess.STOP_LIMIT_INDEX]);
  if (isNaN(limit)) {
    throw 'Bad Argument: must be a number.';
  }
  return limit;
}

/**
 * Logs the error to the spreadsheet.
 *
 * @param {Object} spreadsheetAccess the SpreadsheetAccess instance that
 *    handles the spreadsheet.
 * @param {String} error the error message.
 */
function logError(spreadsheetAccess, error) {
  Logger.log(error);
  spreadsheetAccess.sheet.getRange(spreadsheetAccess.currentRow(),
      spreadsheetAccess.RESULTS_COLUMN_INDEX +
          spreadsheetAccess.START_COLUMN, 1, 1)
  .setValue(error)
  .setFontColor('#c00')
  .setFontSize(8)
  .setFontWeight('bold');
}

/**
 * Logs the results to the spreadsheet.
 *
 * @param {Object} spreadsheetAccess the SpreadsheetAccess instance that
 *    handles the spreadsheet.
 * @param {String} result the result message.
 */
function logResult(spreadsheetAccess, result) {
  spreadsheetAccess.sheet.getRange(spreadsheetAccess.currentRow(),
      spreadsheetAccess.RESULTS_COLUMN_INDEX +
          spreadsheetAccess.START_COLUMN, 1, 1)
  .setValue(result)
  .setFontColor('#444')
  .setFontSize(8)
  .setFontWeight('normal');
}

/**
 * Controls access to the data spreadsheet.
 *
 * @param {String} spreadsheetUrl the spreadsheet url.
 * @param {String} sheetName name of the spreadsheet that contains the bid
 *     rules.
 * @constructor
 */
function SpreadsheetAccess(spreadsheetUrl, sheetName) {

  /**
   * Gets the next row in sequence.
   *
   * @return {?Array.<Object> } the next row, or null if there are no more
   *     rows.
   * @this SpreadsheetAccess
   */
  this.nextRow = function() {
    for (; this.rowIndex < this.cells.length; this.rowIndex++) {
      if (this.cells[this.rowIndex][0]) {
        return this.cells[this.rowIndex++];
      }
    }
    return null;
  };

  /**
   * The current spreadsheet row.
   *
   * @return {Number} the current row.
   * @this SpreadsheetAccess
   */
  this.currentRow = function() {
    return this.rowIndex + this.START_ROW - 1;
  };

  /**
   * The total number of data columns for the spreadsheet.
   *
   * @return {Number} the total number of data columns.
   * @this SpreadsheetAccess
   */
  this.getTotalColumns = function() {
    var totalCols = 0;
    var columns = this.getColumnHeaders();
    for (var i = 0; i < columns.length; i++) {
      if (columns[i].length == 0 || columns[i] == this.RESULTS_COLUMN_HEADER) {
        totalCols = i;
        break;
      }
    }
    return totalCols;
  };

  /**
   * Gets the list of column beaders.
   *
   * @return {Array.<String>} the list of column headers.
   * @this SpreadsheetAccess
   */
  this.getColumnHeaders = function() {
    return this.sheet.getRange(
      this.HEADER_ROW,
      this.START_COLUMN,
      1,
      this.MAX_COLUMNS - this.START_COLUMN + 1).getValues()[0];
  };

  /**
   * Gets the results column index.
   *
   * @return {Number} the results column index.
   * @throws exception if results column is missing.
   * @this SpreadsheetAccess
   */
  this.getResultsColumn = function() {
    var columns = this.getColumnHeaders();
    var totalColumns = this.getTotalColumns();

    if (columns[totalColumns] != 'Results') {
      throw ('Results column is missing.');
    }
    return totalColumns;
  };

  /**
   * Initializes the class methods.
   *
   * @this SpreadsheetAccess
   */
  this.init = function() {

    this.HEADER_ROW = 5;

    this.FIRST_CONDITIONAL_COLUMN = 4;
    this.START_ROW = 6;
    this.START_COLUMN = 2;

    Logger.log('Using spreadsheet - %s.', spreadsheetUrl);
    this.spreadsheet = validateAndGetSpreadsheet(spreadsheetUrl);

    this.sheet = this.spreadsheet.getSheetByName(sheetName);
    this.RESULTS_COLUMN_HEADER = 'Results';

    this.MAX_ROWS = this.sheet.getMaxRows();
    this.MAX_COLUMNS = this.sheet.getMaxColumns();

    this.CUSTOMERID_INDEX = 0;
    this.RULE_INDEX = 1;
    this.ARGUMENT_INDEX = 2;
    this.STOP_LIMIT_INDEX = 3;
    this.RESULTS_COLUMN_INDEX = this.getResultsColumn();


    this.cells = this.sheet.getRange(this.START_ROW, this.START_COLUMN,
                      this.MAX_ROWS, this.MAX_COLUMNS).getValues();
    this.rowIndex = 0;
  };

  this.init();
}

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

Enviar comentários sobre…

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