Register for one of our upcoming Google Ads Scripts workshops.

Gerenciamento de campanhas com base no clima

Lances

Este script mostra uma aplicação específica do gerenciamento de campanhas com base no clima. Clique aqui para uma versão mais genérica deste script que você pode adaptar aos requisitos de gerenciamento da sua campanha.

A demanda por determinados produtos e serviços varia muito, dependendo do clima. Por exemplo, os usuários têm muito mais chances de procurar informações sobre parques de diversão em um dia quente e ensolarado, do que se estiver frio e chovendo. Para uma empresa de parque de diversões, convém aumentar seus lances quando o clima estiver bom. No entanto, fazer isso diariamente exigiria muito trabalho manual. Porém, com os scripts do Google AdWords, é possível recuperar automaticamente as informações sobre o clima e ajustar os lances em questão de minutos.

Este script usa as Planilhas do Google para armazenar a lista de campanhas e seus locais associados. Uma chamada para a OpenWeatherMap API é feita para cada local, e as condições climáticas são calculadas usando algumas regras básicas. Se uma regra for avaliada como verdadeira, um multiplicador de lance do local correspondente será aplicado à segmentação por local da campanha.

Como funciona

O script funciona lendo os dados de uma planilha. A planilha consiste em três páginas individuais:

1. Dados da campanha

Um conjunto de regras determina os modificadores de lance a serem aplicados às campanhas quando uma condição climática for atendida. As colunas obrigatórias nessa página são:

  • Nome da campanha: o nome da campanha a ser modificado.
  • Local do clima: o local cujas condições climáticas devem ser verificadas.
  • Condição climática: a condição climática para a qual essa regra deve ser aplicada.
  • Modificador de lance: o modificador de lance do local a ser aplicado se a condição climática for atendida.
  • Aplicar modificador a: se o modificador de lance deve ser aplicado somente às segmentações geográficas da campanha que correspondem ao "Local do clima" ou a todas as segmentações geográficas da campanha.
  • Ativa: especifique yes para ativar uma regra e no para desativá-la.
O exemplo abaixo mostra diferentes cenários de uso.

A campanha de teste 1 mostra um cenário típico de uso. Digamos que a campanha esteja segmentando Boston, MA. Ela tem duas regras:

  1. Aplique um modificador de lance de 1,3 se o clima em Boston, MA estiver Ensolarado.
  2. Aplique um modificador de lance de 0,8 se o clima em Boston, MA estiver Chuvoso.

A campanha de teste 2 mostra outro cenário típico de uso. Digamos que a campanha esteja segmentando várias cidades nos estados de Nova York e Connecticut, mas que haja interesse somente nos lances com base no clima para as cidades em Connecticut. Ela tem duas regras, e o "Local do clima" de Connecticut está definido como várias cidades (veja abaixo). Dessa forma, somente as cidades de Connecticut segmentadas pela campanha terão os modificadores de lance ajustados. As cidades de Nova York não serão afetadas.

Para a campanha de teste 3, digamos que a campanha segmente muitas cidades da Flórida, mas não o estado como um todo. Como as regras do clima da Florida são definidas para todo o estado, que não é um local que a campanha segmenta explicitamente, o "Aplicar modificador a" está definido como All Geo Targets, de modo que as cidades segmentadas pela campanha sejam afetadas.

Observação: se várias regras correspondem a uma determinada campanha e local, a última regra correspondente tem prevalência.

2. Dados do clima

Esta página define as condições climáticas usadas na página de dados Campanha. As colunas obrigatórias são:

  • Nome da condição: o nome da condição climática (por exemplo, ensolarado).
  • Temperatura: a temperatura em Fahrenheit.
  • Precipitação: a chuva, em milímetros, durante as últimas 3 horas.
  • Vento: a velocidade do vento, em milhas por hora.

A página mostrada acima define duas condições climáticas:

  1. Ensolarado: a temperatura está entre 65 e 80 graus Fahrenheit, precipitação inferior a 1 mm de chuva nas últimas três horas e velocidade do vento inferior a 5 mph.
  2. Chuvoso: a precipitação é superior a 0 mm de chuva nas últimas três horas e a velocidade do vento é inferior a 10 mph.

Como definir as condições climáticas

Ao definir as condições climáticas, convém especificar os seguintes valores:

  • abaixo de x: o valor especificado está abaixo de x (por exemplo, 10)
  • acima de x: o valor especificado está acima de x (por exemplo, 70)
  • x até y: o valor especificado está entre x e y, inclusive (por exemplo, de 65 até 80)

Se você deixar uma célula em branco, os valores desse parâmetro não serão considerados para o cálculo. Por exemplo, na página mostrada acima, a condição climática "Chuvosa" tem uma coluna de temperatura vazia. Assim, a temperatura não será considerada durante o cálculo dessa condição climática.

As condições climáticas são agrupadas com o operando AND no cálculo da condição climática. Para este exemplo, a condição climática "Ensolarada" é avaliada como:

var isSunny = (temperature >= 65 && temperature <= 80) && (precipitation < 1) && (wind < 5);

3. Dados do local do clima

Essa página define os locais do clima usados na página de dados "Campanha" e consiste em apenas duas colunas:

Com o script, você pode especificar vários códigos de segmentação geográfica para um único local do clima. Isso é permitido porque os locais do clima nem sempre são tão granulares quanto as opções de segmentação disponíveis no Google AdWords, e convém mapear um único local do clima para vários locais geográficos. É possível mapear um único local do clima para vários locais geográficos por meio de várias linhas com o mesmo local do clima, mas diferentes códigos geográficos para cada linha.

Na planilha acima, existem três locais do clima definidos:

  1. Boston, MA: código geográfico 10108127
  2. Connecticut: códigos geográficos 1014778, 1014743 e 1014843, que correspondem a várias cidades no estado de Connecticut
  3. Flórida: código geográfico 21142

Segmentação por proximidade

É possível aplicar regras de campanha que usam Matching Geo Targets a regiões de segmentação, proximidades segmentadas ou as duas opções com o uso da sinalização TARGETING. A segmentação por local faz correspondência com os códigos geográficos com os IDs de locais. A segmentação por proximidade verifica se as coordenadas de latitude e longitude especificadas estão no raio de proximidade usando a fórmula Haversine.

Lógica do script

O script começa lendo as regras de todas as três páginas. Em seguida, ele tenta executar cada regra com base na página "Campanha" na sequência.

Para cada regra executada, o script verifica se a campanha segmenta o local especificado. Em caso positivo, o script recupera o modificador de lance atual. Em seguida, as condições climáticas para esse local são recuperadas por meio de uma chamada para a API OpenWeatherMap. Depois, as regras de condição climática são avaliadas para ver se a condição climática para o local corresponde ao que é especificado na regra. Em caso positivo, e se o novo modificador de lance for diferente do modificador de lance atual, o script alterará o modificador de lance para esse local.

Nenhuma alteração será feita se a condição climática não for correspondente, os valores do modificador de lance forem iguais ou se o "Aplicar modificador a" da regra for Matching Geo Targets, mas a campanha não segmentar os locais mapeados pela regra.

Configuração

  • Inscreva-se para uma chave da API em http://openweathermap.org/appid.
  • Faça uma cópia da planilha modelo e edite sua campanha e suas regras do clima.
  • Crie um novo script com o código-fonte abaixo.
  • Atualize as variáveis OPEN_WEATHER_MAP_API_KEY, SPREADSHEET_URL e TARGETING no script.
  • Programe a execução conforme 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 Bid By Weather
 *
 * @overview The Bid By Weather script adjusts campaign bids by weather
 *     conditions of their associated locations. See
 *     https://developers.google.com/adwords/scripts/docs/solutions/weather-based-campaign-management#bid-by-weather
 *     for more details.
 *
 * @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 1.2.2
 *
 * @changelog
 * - version 1.2.2
 *   - Add support for video and shopping campaigns.
 * - version 1.2.1
 *   - Added validation for external spreadsheet setup.
 * - version 1.2
 *   - Added proximity based targeting.  Targeting flag allows location
 *     targeting, proximity targeting or both.
 * - version 1.1
 *   - Added flag allowing bid adjustments on all locations targeted by
 *     a campaign rather than only those that match the campaign rule
 * - version 1.0
 *   - Released initial version.
 */

// Register for an API key at http://openweathermap.org/appid
// and enter the key below.
var OPEN_WEATHER_MAP_API_KEY = 'INSERT_OPEN_WEATHER_MAP_API_KEY_HERE';

// Create a copy of https://goo.gl/7b5mYI and enter the URL below.
var SPREADSHEET_URL = 'INSERT_SPREADSHEET_URL_HERE';

// A cache to store the weather for locations already lookedup earlier.
var WEATHER_LOOKUP_CACHE = {};

// Flag to pick which kind of targeting "LOCATION", "PROXIMITY", or "ALL".
var TARGETING = 'ALL';


/**
 * The code to execute when running the script.
 */
function main() {
  validateApiKey();
  // Load data from spreadsheet.
  var spreadsheet = validateAndGetSpreadsheet(SPREADSHEET_URL);
  var campaignRuleData = getSheetData(spreadsheet, 1);
  var weatherConditionData = getSheetData(spreadsheet, 2);
  var geoMappingData = getSheetData(spreadsheet, 3);

  // Convert the data into dictionaries for convenient usage.
  var campaignMapping = buildCampaignRulesMapping(campaignRuleData);
  var weatherConditionMapping =
      buildWeatherConditionMapping(weatherConditionData);
  var locationMapping = buildLocationMapping(geoMappingData);

  // Apply the rules.
  for (var campaignName in campaignMapping) {
    applyRulesForCampaign(campaignName, campaignMapping[campaignName],
        locationMapping, weatherConditionMapping);
  }
}

/**
 * Retrieves the data for a worksheet.
 *
 * @param {Object} spreadsheet The spreadsheet.
 * @param {number} sheetIndex The sheet index.
 * @return {Array} The data as a two dimensional array.
 */
function getSheetData(spreadsheet, sheetIndex) {
  var sheet = spreadsheet.getSheets()[sheetIndex];
  var range =
      sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn());
  return range.getValues();
}

/**
 * Builds a mapping between the list of campaigns and the rules
 * being applied to them.
 *
 * @param {Array} campaignRulesData The campaign rules data, from the
 *     spreadsheet.
 * @return {Object.<string, Array.<Object>> } A map, with key as campaign name,
 *     and value as an array of rules that apply to this campaign.
 */
function buildCampaignRulesMapping(campaignRulesData) {
  var campaignMapping = {};
  for (var i = 0; i < campaignRulesData.length; i++) {
    // Skip rule if not enabled.

    if (campaignRulesData[i][5].toLowerCase() == 'yes') {
      var campaignName = campaignRulesData[i][0];
      var campaignRules = campaignMapping[campaignName] || [];
      campaignRules.push({
          'name': campaignName,

          // location for which this rule applies.
          'location': campaignRulesData[i][1],

          // the weather condition (e.g. Sunny).
          'condition': campaignRulesData[i][2],

          // bid modifier to be applied.
          'bidModifier': campaignRulesData[i][3],

          // whether bid adjustments should by applied only to geo codes
          // matching the location of the rule or to all geo codes that
          // the campaign targets.
          'targetedOnly': campaignRulesData[i][4].toLowerCase() ==
                          'matching geo targets'
      });
      campaignMapping[campaignName] = campaignRules;
    }
  }
  Logger.log('Campaign Mapping: %s', campaignMapping);
  return campaignMapping;
}

/**
 * Builds a mapping between a weather condition name (e.g. Sunny) and the rules
 * that correspond to that weather condition.
 *
 * @param {Array} weatherConditionData The weather condition data from the
 *      spreadsheet.
 * @return {Object.<string, Array.<Object>>} A map, with key as a weather
 *     condition name, and value as the set of rules corresponding to that
 *     weather condition.
 */
function buildWeatherConditionMapping(weatherConditionData) {
  var weatherConditionMapping = {};

  for (var i = 0; i < weatherConditionData.length; i++) {
    var weatherConditionName = weatherConditionData[i][0];
    weatherConditionMapping[weatherConditionName] = {
      // Condition name (e.g. Sunny)
      'condition': weatherConditionName,

      // Temperature (e.g. 50 to 70)
      'temperature': weatherConditionData[i][1],

      // Precipitation (e.g. below 70)
      'precipitation': weatherConditionData[i][2],

      // Wind speed (e.g. above 5)
      'wind': weatherConditionData[i][3]
    };
  }
  Logger.log('Weather condition mapping: %s', weatherConditionMapping);
  return weatherConditionMapping;
}

/**
 * Builds a mapping between a location name (as understood by OpenWeatherMap
 * API) and a list of geo codes as identified by AdWords scripts.
 *
 * @param {Array} geoTargetData The geo target data from the spreadsheet.
 * @return {Object.<string, Array.<Object>>} A map, with key as a locaton name,
 *     and value as an array of geo codes that correspond to that location
 *     name.
 */
function buildLocationMapping(geoTargetData) {
  var locationMapping = {};
  for (var i = 0; i < geoTargetData.length; i++) {
    var locationName = geoTargetData[i][0];
    var locationDetails = locationMapping[locationName] || {
      'geoCodes': []      // List of geo codes understood by AdWords scripts.
    };

    locationDetails.geoCodes.push(geoTargetData[i][1]);
    locationMapping[locationName] = locationDetails;
  }
  Logger.log('Location Mapping: %s', locationMapping);
  return locationMapping;
}

/**
 * Applies rules to a campaign.
 *
 * @param {string} campaignName The name of the campaign.
 * @param {Object} campaignRules The details of the campaign. See
 *     buildCampaignMapping for details.
 * @param {Object} locationMapping Mapping between a location name (as
 *     understood by OpenWeatherMap API) and a list of geo codes as
 *     identified by AdWords scripts. See buildLocationMapping for details.
 * @param {Object} weatherConditionMapping Mapping between a weather condition
 *     name (e.g. Sunny) and the rules that correspond to that weather
 *     condition. See buildWeatherConditionMapping for details.
 */
function applyRulesForCampaign(campaignName, campaignRules, locationMapping,
                               weatherConditionMapping) {
  for (var i = 0; i < campaignRules.length; i++) {
    var bidModifier = 1;
    var campaignRule = campaignRules[i];

    // Get the weather for the required location.
    var locationDetails = locationMapping[campaignRule.location];
    var weather = getWeather(campaignRule.location);
    Logger.log('Weather for %s: %s', locationDetails, weather);

    // Get the weather rules to be checked.
    var weatherConditionName = campaignRule.condition;
    var weatherConditionRules = weatherConditionMapping[weatherConditionName];

    // Evaluate the weather rules.
    if (evaluateWeatherRules(weatherConditionRules, weather)) {
      Logger.log('Matching Rule found: Campaign Name = %s, location = %s, ' +
          'weatherName = %s,weatherRules = %s, noticed weather = %s.',
          campaignRule.name, campaignRule.location,
          weatherConditionName, weatherConditionRules, weather);
      bidModifier = campaignRule.bidModifier;

      if (TARGETING == 'LOCATION' || TARGETING == 'ALL') {
        // Get the geo codes that should have their bids adjusted.
        var geoCodes = campaignRule.targetedOnly ?
          locationDetails.geoCodes : null;
        adjustBids(campaignName, geoCodes, bidModifier);
      }

      if (TARGETING == 'PROXIMITY' || TARGETING == 'ALL') {
        var location = campaignRule.targetedOnly ? campaignRule.location : null;
        adjustProximityBids(campaignName, location, bidModifier);
      }

    }
  }
  return;
}

/**
 * Converts a temperature value from kelvin to fahrenheit.
 *
 * @param {number} kelvin The temperature in Kelvin scale.
 * @return {number} The temperature in Fahrenheit scale.
 */
function toFahrenheit(kelvin) {
  return (kelvin - 273.15) * 1.8 + 32;
}

/**
 * Evaluates the weather rules.
 *
 * @param {Object} weatherRules The weather rules to be evaluated.
 * @param {Object.<string, string>} weather The actual weather.
 * @return {boolean} True if the rule matches current weather conditions,
 *     False otherwise.
 */
function evaluateWeatherRules(weatherRules, weather) {
  // See http://bugs.openweathermap.org/projects/api/wiki/Weather_Data
  // for values returned by OpenWeatherMap API.
  var precipitation = 0;
  if (weather.rain && weather.rain['3h']) {
    precipitation = weather.rain['3h'];
  }
  var temperature = toFahrenheit(weather.main.temp);
  var windspeed = weather.wind.speed;

  return evaluateMatchRules(weatherRules.temperature, temperature) &&
      evaluateMatchRules(weatherRules.precipitation, precipitation) &&
      evaluateMatchRules(weatherRules.wind, windspeed);
}

/**
 * Evaluates a condition for a value against a set of known evaluation rules.
 *
 * @param {string} condition The condition to be checked.
 * @param {Object} value The value to be checked.
 * @return {boolean} True if an evaluation rule matches, false otherwise.
 */
function evaluateMatchRules(condition, value) {
  // No condition to evaluate, rule passes.
  if (condition == '') {
    return true;
  }
  var rules = [matchesBelow, matchesAbove, matchesRange];

  for (var i = 0; i < rules.length; i++) {
    if (rules[i](condition, value)) {
      return true;
    }
  }
  return false;
}

/**
 * Evaluates whether a value is below a threshold value.
 *
 * @param {string} condition The condition to be checked. (e.g. below 50).
 * @param {number} value The value to be checked.
 * @return {boolean} True if the value is less than what is specified in
 * condition, false otherwise.
 */
function matchesBelow(condition, value) {
  conditionParts = condition.split(' ');

  if (conditionParts.length != 2) {
    return false;
  }

  if (conditionParts[0] != 'below') {
    return false;
  }

  if (value < conditionParts[1]) {
    return true;
  }
  return false;
}

/**
 * Evaluates whether a value is above a threshold value.
 *
 * @param {string} condition The condition to be checked. (e.g. above 50).
 * @param {number} value The value to be checked.
 * @return {boolean} True if the value is greater than what is specified in
 *     condition, false otherwise.
 */
function matchesAbove(condition, value) {
  conditionParts = condition.split(' ');

  if (conditionParts.length != 2) {
    return false;
  }

  if (conditionParts[0] != 'above') {
    return false;
  }

  if (value > conditionParts[1]) {
    return true;
  }
  return false;
}

/**
 * Evaluates whether a value is within a range of values.
 *
 * @param {string} condition The condition to be checked (e.g. 5 to 18).
 * @param {number} value The value to be checked.
 * @return {boolean} True if the value is in the desired range, false otherwise.
 */
function matchesRange(condition, value) {
  conditionParts = condition.replace('\w+', ' ').split(' ');

  if (conditionParts.length != 3) {
    return false;
  }

  if (conditionParts[1] != 'to') {
    return false;
  }

  if (conditionParts[0] <= value && value <= conditionParts[2]) {
    return true;
  }
  return false;
}

/**
 * Retrieves the weather for a given location, using the OpenWeatherMap API.
 *
 * @param {string} location The location to get the weather for.
 * @return {Object.<string, string>} The weather attributes and values, as
 *     defined in the API.
 */
function getWeather(location) {
  if (location in WEATHER_LOOKUP_CACHE) {
    Logger.log('Cache hit...');
    return WEATHER_LOOKUP_CACHE[location];
  }

  var url = Utilities.formatString(
      'http://api.openweathermap.org/data/2.5/weather?APPID=%s&q=%s',
      encodeURIComponent(OPEN_WEATHER_MAP_API_KEY),
      encodeURIComponent(location));
  var response = UrlFetchApp.fetch(url);
  if (response.getResponseCode() != 200) {
    throw Utilities.formatString(
        'Error returned by API: %s, Location searched: %s.',
        response.getContentText(), location);
  }

  var result = JSON.parse(response.getContentText());

  // OpenWeatherMap's way of returning errors.
  if (result.cod != 200) {
    throw Utilities.formatString(
        'Error returned by API: %s,  Location searched: %s.',
        response.getContentText(), location);
  }

  WEATHER_LOOKUP_CACHE[location] = result;
  return result;
}

/**
 * Adjusts the bidModifier for a list of geo codes for a campaign.
 *
 * @param {string} campaignName The name of the campaign.
 * @param {Array} geoCodes The list of geo codes for which bids should be
 *     adjusted.  If null, all geo codes on the campaign are adjusted.
 * @param {number} bidModifier The bid modifier to use.
 */
function adjustBids(campaignName, geoCodes, bidModifier) {
  // Get the campaign.
  var campaign = getCampaign(campaignName);
  if (!campaign) return null;

  // Get the targeted locations.
  var locations = campaign.targeting().targetedLocations().get();
  while (locations.hasNext()) {
    var location = locations.next();
    var currentBidModifier = location.getBidModifier().toFixed(2);

    // Apply the bid modifier only if the campaign has a custom targeting
    // for this geo location or if all locations are to be modified.
    if (!geoCodes || (geoCodes.indexOf(location.getId()) != -1 &&
      currentBidModifier != bidModifier)) {
        Logger.log('Setting bidModifier = %s for campaign name = %s, ' +
            'geoCode = %s. Old bid modifier is %s.', bidModifier,
            campaignName, location.getId(), currentBidModifier);
        location.setBidModifier(bidModifier);
    }
  }
}

/**
 * Adjusts the bidModifier for campaigns targeting by proximity location
 * for a given weather location.
 *
 * @param {string} campaignName The name of the campaign.
 * @param {string} weatherLocation The weather location for which bids should be
 *     adjusted.  If null, all proximity locations on the campaign are adjusted.
 * @param {number} bidModifier The bid modifier to use.
 */
function adjustProximityBids(campaignName, weatherLocation, bidModifier) {
  // Get the campaign.
  var campaign = getCampaign(campaignName);
  if(campaign === null) return;

  // Get the proximity locations.
  var proximities = campaign.targeting().targetedProximities().get();
  while (proximities.hasNext()) {
    var proximity = proximities.next();
    var currentBidModifier = proximity.getBidModifier().toFixed(2);

    // Apply the bid modifier only if the campaign has a custom targeting
    // for this geo location or if all locations are to be modified.
    if (!weatherLocation ||
        (weatherNearProximity(proximity, weatherLocation) &&
      currentBidModifier != bidModifier)) {
        Logger.log('Setting bidModifier = %s for campaign name = %s, with ' +
            'weatherLocation = %s in proximity area. Old bid modifier is %s.',
            bidModifier, campaignName, weatherLocation, currentBidModifier);
        proximity.setBidModifier(bidModifier);
      }
  }
}

/**
 * Checks if weather location is within the radius of the proximity location.
 *
 * @param {Object} proximity The targeted proximity of campaign.
 * @param {string} weatherLocation Name of weather location to check within
 * radius.
 * @return {boolean} Returns true if weather location is within radius.
 */
function weatherNearProximity(proximity, weatherLocation) {
  // See https://en.wikipedia.org/wiki/Haversine_formula for details on how
  // to compute spherical distance.
  var earthRadiusInMiles = 3960.0;
  var degreesToRadians = Math.PI / 180.0;
  var radiansToDegrees = 180.0 / Math.PI;
  var kmToMiles = 0.621371;

  var radiusInMiles = proximity.getRadiusUnits() == 'MILES' ?
    proximity.getRadius() : proximity.getRadius() * kmToMiles;

  // Compute the change in latitude degrees for the radius.
  var deltaLat = (radiusInMiles / earthRadiusInMiles) * radiansToDegrees;
  // Find the radius of a circle around the earth at given latitude.
  var r = earthRadiusInMiles * Math.cos(proximity.getLatitude() *
      degreesToRadians);
  // Compute the change in longitude degrees for the radius.
  var deltaLon = (radiusInMiles / r) * radiansToDegrees;

  // Retrieve weather location for lat/lon coordinates.
  var weather = getWeather(weatherLocation);
  // Check if weather condition is within the proximity boundaries.
  return (weather.coord.lat >= proximity.getLatitude() - deltaLat &&
          weather.coord.lat <= proximity.getLatitude() + deltaLat &&
          weather.coord.lon >= proximity.getLongitude() - deltaLon &&
          weather.coord.lon <= proximity.getLongitude() + deltaLon);
}

/**
 * Finds a campaign by name, whether it is a regular, video, or shopping
 * campaign, by trying all in sequence until it finds one.
 *
 * @param {string} campaignName The campaign name to find.
 * @return {Object} The campaign found, or null if none was found.
 */
function getCampaign(campaignName) {
  var selectors = [AdWordsApp.campaigns(), AdWordsApp.videoCampaigns(),
      AdWordsApp.shoppingCampaigns()];
  for(var i = 0; i < selectors.length; i++) {
    var campaignIter = selectors[i].
        withCondition('CampaignName = "' + campaignName + '"').
        get();
    if (campaignIter.hasNext()) {
      return campaignIter.next();
    }
  }
  return null;
}

/**
 * DO NOT EDIT ANYTHING BELOW THIS LINE.
 * Please modify your spreadsheet URL and API key at the top of the file only.
 */

/**
 * 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 == 'INSERT_SPREADSHEET_URL_HERE') {
    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;
}

/**
 * Validates the provided API key to make sure that it's not the default. Throws
 * a descriptive error message if validation fails.
 *
 * @throws {Error} If the configured API key hasn't been set.
 */
function validateApiKey() {
  if (OPEN_WEATHER_MAP_API_KEY == 'INSERT_OPEN_WEATHER_MAP_API_KEY_HERE') {
    throw new Error('Please specify a valid API key for OpenWeatherMap. You ' +
        'can acquire one here: http://openweathermap.org/appid');
  }
}

Genérico

Este é um script genérico que você pode personalizar para fazer o gerenciamento de campanhas com base no clima. Clique aqui para o script "Lance por clima", um script especializado voltado para o gerenciamento de lances com base no clima.

O script de lance por clima mostra como gerenciar os lances das suas campanhas dependendo das condições climáticas das regiões de segmentação. No entanto, este script só afeta o gerenciamento de lances e atualizá-lo para realizar uma tarefa de gerenciamento de campanha diferente (por exemplo, pausar um grupo de campanhas) não é muito simples. Este guia apresenta um script de gerenciamento de clima mais genérico, com o qual você pode realizar tarefas de gerenciamento de campanhas com base nas informações do clima.

Como recuperar dados do clima

O script recupera informações do clima de servidores OpenWeatherMap. Você pode solicitar dados do clima atuais ou a previsão da semana. Você pode fornecer nomes de cidades ao OpenWeatherMap por meio de formulários gratuitos:

// Get current weather information for New York City.
var nyWeather = getWeatherForLocation("New York City, US");

// Get the weather forecast for the next week in Toronto.
var torontoWeather = getWeatherForecastForLocation("Toronto, CA");

Como funciona o formato de dados

As informações do clima retornadas pelo método getWeatherForLocation() têm este formato:

{
  name: "New York",
  weather: {
    clouds: 48,
    windspeed: 2.66,
    status: {
      summary: "Clouds",
      id: 802,
      description: "scattered clouds"
    },
    snow: 0.0,
    rain: 0.0,
    temperature: 286.16
  },
  country: "United States of America"
}

As diversas propriedades podem ser interpretadas desta forma:

Campo Detalhe
name Nome canônico do local, conforme retornado pelo OpenWeatherMap.
clouds Porcentagem de nuvens.
windspeed Velocidade do vento, em m/s.
snow Volume de neve nas últimas três horas em mm.
rain Volume de chuva nas últimas três horas em mm.
summary Um resumo do clima. Consulte http://openweathermap.org/weather-conditions para os valores válidos.
description Uma descrição do clima. Consulte http://openweathermap.org/weather-conditions para os valores válidos.
id Um ID da condição climática, conforme retornado pelo OpenWeatherMap. Consulte http://openweathermap.org/weather-conditions para os valores válidos.
temperature A temperatura na escala Kelvin.

De forma semelhante, as informações da previsão do tempo retornadas pelo método getWeatherForecastForLocation() têm este formato:

{
  name: "Toronto",
  country: "CA"
  forecast: {
    "20150416": {
       clouds: 0.0,
       windspeed: 2.06,
       status: {
         summary: "Rain",
         id: 500,
         description: "light rain"
       },
       snow: 0.0,
       rain: 1.89,
       temperature: 0.0
    },
    "20150417": {
      ...
    }
  }
}

As propriedades no feed da previsão têm o mesmo significado que o feed do clima. A propriedade de previsão adicional é um mapa, sendo a chave a data no formato aaaaMMdd e o valor os dados da previsão do tempo do dia. Por padrão, as previsões do tempo para o período de sete dias são retornados. O script simplifica o JSON que é retornado pela OpenWeatherMap API.

Como gerenciar suas campanhas

Veja alguns exemplos de como gerenciar suas campanhas com base nestas informações do clima:

Clima atual

Exemplo 1: usar o resumo do clima fornecido pelo OpenWeatherMap.

var nyWeather = getWeatherForLocation("New York City, US");
if (nyWeather.weather.status.summary === "Rain") {
  var campaign = AdWordsApp.campaigns()
      .withCondition("CampaignName = 'Sunny Trips'")
      .get()
      .next();
  campaign.pause();
}

Exemplo 2: verificar parâmetros de clima mais específicos.

var nyWeather = getWeatherForLocation("New York City, US");
if (nyWeather.weather.snow > 5 && nyWeather.weather.temperature < 273) {
  var adGroup = AdWordsApp.adGroups()
     .withCondition("CampaignName = 'New York Shoes'")
     .withCondition("AdGroupName='Snow boots'")
     .get()
     .next();
  adGroup.bidding().setCpc(adGroup.bidding().getCpc() + 0.3);
}

Previsão do tempo

Exemplo 1: usar o resumo do clima fornecido pelo OpenWeatherMap.

var nyWeather = getWeatherForecastForLocation("New York City, US");
var rainyDays = 0;
for (var date in nyWeather.forecast) {
  var forecast = nyWeather.forecast[date];
  if (forecast.status.summary === "Rain") {
    rainyDays = rainyDays + 1;
  }
}

if (rainyDays > 4) {
  var campaign = AdWordsApp.campaigns()
      .withCondition("CampaignName = 'Sunny Trips'")
      .get()
      .next();
  campaign.pause();
}

Exemplo 2: verificar parâmetros de clima mais específicos.

var nyWeather = getWeatherForecastForLocation("New York City, US");
var coldDays = 0;
for (var date in nyWeather.forecast) {
  var forecast = nyWeather.forecast[date];

  if (forecast.snow > 5 && forecast.temperature < 273) {
    coldDays = coldDays + 1;
  }
}

if (coldDays > 4) {
  var adGroup = AdWordsApp.adGroups()
      .withCondition("CampaignName = 'New York Shoes'")
      .withCondition("AdGroupName='Snow boots'")
      .get()
      .next();
  adGroup.bidding().setCpc(adGroup.bidding().getCpc() + 0.3);
}

Configuração

  • Inscreva-se para uma chave da API em http://openweathermap.org/appid.
  • Crie um novo script com o código-fonte abaixo.
  • Atualize o OPEN_WEATHER_MAP_API_KEY
  • Remova o comentário do bloco de código adequado no método main() dependendo se você quer executar o script em uma conta de anunciante ou da MCC.
  • Remova o comentário do bloco de código adequado no método processSingleAccount() dependendo se você quer usar as informações do clima atuais ou as informações da previsão.
  • Substitua os conteúdos dos métodos manageCampaignsBasedOnCurrentWeather() ou manageCampaignsBasedOnWeatherForecast() pela sua lógica de gerenciamento de campanhas.
  • Agende a exibição conforme 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 Weather Based Campaign Management
 *
 * @overview The Weather Based Campaign management script allows you to perform
 *     various campaign management tasks based on weather information. See
 *     https://developers.google.com/adwords/scripts/docs/solutions/weather-based-campaign-management#generic-weather
 *     for more details.
 *
 * @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 1.0
 *
 * @changelog
 * - version 1.0
 *   - Released initial version.
 */

// Register for an API key at http://openweathermap.org/appid
// and enter the key below.
var OPEN_WEATHER_MAP_API_KEY = 'INSERT_OPEN_WEATHER_MAP_API_KEY_HERE';

var OPEN_WEATHER_MAP_SERVER_URL = 'http://api.openweathermap.org/data/2.5';
var FORECAST_ENDPOINT = OPEN_WEATHER_MAP_SERVER_URL + '/forecast/daily';
var WEATHER_ENDPOINT = OPEN_WEATHER_MAP_SERVER_URL + '/weather';

function main() {
  // Uncomment the following lines if you are writing code for a single account.
  // processSingleAccount();

  // Uncomment the following lines if you are writing code for an MCC account.
  // var accounts = MccApp.accounts().withIds(['1234567890', '3456789012']);
  // accounts.executeInParallel("processSingleAccount");
}

/**
 * Process a single account.
 */
function processSingleAccount() {
  // Uncomment this block if you want to do campaign management based on
  // current weather.
  // manageCampaignsBasedOnCurrentWeather();

  // Uncomment this block if you want to do campaign management based on
  // weather forecast.
  // manageCampaignsBasedOnWeatherForecast();
}

/**
 * Manage your campaigns based on current weather. The contents of
 * this method are for your reference; replace it with your campaign
 * management logic.
 */
function manageCampaignsBasedOnCurrentWeather() {
  var nyWeather = getWeatherForLocation('New York City, US');

  // Example 1: Use weather summary provided by OpenWeathermap.
  // See http://openweathermap.org/weather-conditions for more details.
  if (nyWeather.weather.status.summary === 'Rain') {
    // Add your logic here.
  }

  // Example 2: Check more specific weather parameters.
  if (nyWeather.weather.snow > 5 && nyWeather.weather.temperature < 273) {
    // Add your logic here.
  }
}

/**
 * Manage your campaigns based on weather forecast. The contents of
 * this method are for your reference; replace it with your campaign
 * management logic.
 */
function manageCampaignsBasedOnWeatherForecast() {
  var nyWeather = getWeatherForecastForLocation('Toronto, CA');

  // Example 1: Use weather summary provided by OpenWeathermap.
  var rainyDays = 0;
  for (var date in nyWeather.forecast) {
    var forecast = nyWeather.forecast[date];
    if (forecast.status.summary === 'Rain') {
      rainyDays = rainyDays + 1;
    }
  }

  if (rainyDays > 4) {
    // Add your logic here.
  }

  // Example 2: Check more specific weather parameters.
  var coldDays = 0;
  for (var date in nyWeather.forecast) {
    var forecast = nyWeather.forecast[date];

    if (forecast.snow > 5 && forecast.temperature < 273) {
      coldDays = coldDays + 1;
    }
  }

  if (coldDays > 4) {
    // Add your logic here.
  }
}

/**
 * Make a call to the OpenWeatherMap server.
 *
 * @param {string} endpoint the server endpoint.
 * @param {string} location the location for which weather
 *     information is retrieved.
 * @return {Object} the server response.
 */
function callWeatherServer(endpoint, location) {
    var url = Utilities.formatString(
      '%s?APPID=%s&q=%s',
      endpoint,
      encodeURIComponent(OPEN_WEATHER_MAP_API_KEY),
      encodeURIComponent(location));
  var response = UrlFetchApp.fetch(url);
  if (response.getResponseCode() != 200) {
    throw Utilities.formatString(
        'Error returned by API: %s, Location searched: %s.',
        response.getContentText(), location);
  }

  var result = JSON.parse(response.getContentText());

  // OpenWeatherMap's way of returning errors.
  if (result.cod != 200) {
    throw Utilities.formatString(
        'Error returned by API: %s,  Location searched: %s.',
        response.getContentText(), location);
  }
  return result;
}

/**
 * Parse the weather response from the OpenWeatherMap server.
 *
 * @param {Object} weather the weather information from
 *     OpenWeatherMap server.
 * @return {Object} the parsed weather response.
 */
function parseWeather(weather) {
  var retval = {
    'rain': 0,
    'temperature': 0,
    'windspeed': 0,
    'snow': 0,
    'clouds': 0,
    'status': {
      'id': 0,
      'summary': '',
      'description': ''
    }
  };

  if (weather.rain) {
    if (typeof weather.rain === 'object' && weather.rain['3h']) {
      retval.rain = weather.rain['3h'];
    } else {
      retval.rain = weather.rain;
    }
  }

  if (weather.snow) {
    if (typeof weather.snow === 'object' && weather.snow['3h']) {
      retval.snow = weather.snow['3h'];
    } else {
      retval.snow = weather.snow;
    }
  }

  if (weather.clouds && weather.clouds.all) {
    retval.clouds = weather.clouds.all;
  }

  if (weather.main) {
    retval.temperature = weather.main.temp.toFixed(2);
  } else if (main.temp) {
    retval.temperature = weather.temp.toFixed(2);
  }

  if (weather.wind) {
    retval.windspeed = weather.wind.speed;
  } else if (weather.speed) {
    retval.windspeed = weather.speed;
  }

  if (weather.weather && weather.weather.length > 0) {
    retval.status.id = weather.weather[0].id;
    retval.status.summary = weather.weather[0].main;
    retval.status.description = weather.weather[0].description;
  }
  return retval;
}

/**
 * Get the weather forecast for a location for the next 7 days.
 *
 * @param {string} location the location for which weather
 *     forecast information is retrieved.
 * @return {Object} the parsed weather response.
 */
function getWeatherForecastForLocation(location) {
  var result = callWeatherServer(FORECAST_ENDPOINT, location);

  var retval = {
    'name': result.city.name,
    'country': result.city.country,
    'forecast': {
    }
  };

  for (var i = 0; i < result.list.length; i++) {
    var forecast = result.list[i];
    var date = formatDate(forecast.dt);
    retval.forecast[date] = parseWeather(forecast);
  }

  return retval;
}

/**
 * Get the current weather information for a location.
 *
 * @param {string} location the location for which weather
 *     information is retrieved.
 * @return {Object} the parsed weather response.
 */
function getWeatherForLocation(location) {
  var result = callWeatherServer(WEATHER_ENDPOINT, location);

  var retval = {
    'name': result.name,
    'country': result.sys.country,
    'weather': parseWeather(result)
  };

  return retval;
}

/**
 * Formats the date in yyyyMMdd format.
 *
 * @param {Number} dt unix timestamp from OpenWeatherMap server.
 * @return {string} the formatted date.
 */
function formatDate(dt) {
  var date = new Date(dt * 1000);
  return Utilities.formatDate(date, 'GMT', 'yyyyMMdd');
}

Enviar comentários sobre…

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