Wetterabhängige Kampagnenverwaltung

Gebote

Dieses Skript zeigt eine spezielle Anwendung der wetterabhängigen Kampagnenverwaltung. Eine allgemeinere Version dieses Skripts können Sie an die Anforderungen Ihrer eigenen Kampagnenverwaltung anpassen.

Die Nachfrage nach bestimmten Produkten und Dienstleistungen ist stark wetterabhängig. Nutzer suchen beispielsweise eher an einem warmen, sonnigen Tag nach Informationen zu Freizeitparks als an einem kalten Regentag. Der Betreiber eines Freizeitparks möchte daher seine Gebote bei gutem Wetter vermutlich erhöhen. Es wäre jedoch sehr aufwendig, sich täglich selbst darum zu kümmern. Mit AdWords-Skripts ist es innerhalb weniger Minuten möglich, programmatisch Wetterdaten abzufragen und Gebote anzupassen.

Das Skript verwendet Google Tabellen, um die Liste der Kampagnen und der zugeordneten Standorte zu speichern. Für jeden Standort wird die OpenWeatherMap API aufgerufen und das Wetter anhand einfacher Regeln berechnet. Wenn eine Regel den Wert "true" zurückgibt, wird eine entsprechende Gebotsanpassung für den Standort auf die geografische Ausrichtung der Kampagne angewendet.

Funktionsweise

Das Skript liest die Daten der Tabellen in drei Tabellenblättern aus:

1. Kampagnendaten

Bei Gebotsanpassungen, die bei bestimmten Wetterlagen auf Kampagnen angewendet werden, werden mehrere Regeln berücksichtigt. Folgende Spalten werden in dieser Tabelle benötigt:

  • "Campaign Name": der Name der zu ändernden Kampagne
  • "Weather Location": der Standort, für den die Wetterlage ermittelt werden soll
  • "Weather Condition": die Wetterlage, bei der diese Regel angewendet wird
  • "Bid Modifier": die Gebotsanpassung für den Standort, die bei entsprechender Wetterlage angewendet wird
  • "Apply Modifier To": gibt an, ob die Gebotsanpassung nur auf die geografischen Ziele der Kampagne angewendet werden soll, die mit "Weather Location" übereinstimmen, oder auf alle geografischen Ziele der Kampagne
  • "Enabled": Bei Yes wird die Regel aktiviert, bei No wird sie deaktiviert.
Hier finden Sie einige Beispielkampagnen.

Testkampagne 1 zeigt ein typisches Anwendungsszenario. Die Kampagne ist auf Boston, Massachusetts ausgerichtet, und es werden zwei Regeln berücksichtigt:

  1. Gebotsanpassung 1,3 anwenden, wenn das Wetter in Boston, Massachusetts sonnig ist.
  2. Gebotsanpassung 0,8 anwenden, wenn das Wetter in Boston, Massachusetts regnerisch ist.

Testkampagne 2 zeigt ein weiteres typisches Anwendungsszenario. Die Kampagne ist auf mehrere Städte in New York und Connecticut ausgerichtet, die wetterabhängige Gebotseinstellung ist jedoch nur für die Städte in Connecticut von Interesse. Sie berücksichtigt zwei Regeln und bei Connecticut wurden für "Weather Location" mehrere Städte angegeben (siehe unten). So wird die Gebotsanpassung nur auf Städte in Connecticut angewendet, auf die die Kampagne ausgerichtet ist. Die Städte in New York sind davon nicht betroffen.

Testkampagne 3 ist auf viele Städte in Florida ausgerichtet, jedoch nicht auf den gesamten Bundesstaat. Da die Wetterregeln für Florida dem gesamten Bundesstaat zugeordnet sind, was nicht exakt der Ausrichtung der Kampagne entspricht, wird "Apply Modifier To" auf All Geo Targets festgelegt, sodass die Städte einbezogen sind, auf die die Kampagne ausgerichtet ist.

Hinweis: Wenn für eine bestimmte Kampagne und einen bestimmten Standort mehrere Regeln passen, wird die letzte passende Regel angewendet.

2. Wetterdaten

In der Tabelle werden die Wetterlagen festgelegt, die in der Tabelle mit den Kampagnendaten verwendet werden. Folgende Spalten sind erforderlich:

  • "Condition Name": der Name der Wetterlage (beispielsweise sonnig)
  • "Temperature": die Temperatur in Grad Fahrenheit
  • "Precipitation": die Regenmenge in Millimetern während der letzten drei Stunden
  • "Wind": die Windgeschwindigkeit in Meilen pro Stunde

Die Tabelle oben enthält zwei Wetterlagen:

  1. "Sunny": Die Temperatur liegt zwischen 65 und 80 °Fahrenheit, der Niederschlag liegt unter 1 mm Regen in den letzten 3 Stunden und die Windgeschwindigkeit beträgt weniger als 5 mph.
  2. "Rainy": Der Niederschlag beträgt mehr als 0 mm Regen in den letzten 3 Stunden und die Windgeschwindigkeit liegt über 10 mph.

Wetterlagen definieren

So definieren Sie Wetterlagen:

  • unter x: der angegebene Wert liegt unter x (beispielsweise unter 10)
  • über x: der angegebene Wert liegt über x (beispielsweise über 70)
  • x bis y: der angegebene Wert liegt zwischen x und einschließlich y (beispielsweise 65 bis 80)

Wenn Sie eine Zelle leer lassen, wird dieser Parameter bei der Berechnung nicht berücksichtigt. In der Tabelle oben ist beispielsweise die Temperaturzelle für die Wetterlage "Rainy" leer. Die Temperatur wird daher bei der Berechnung dieser Wetterlage nicht berücksichtigt.

Bei der Berechnung der Wetterlage werden die einzelnen Bedingungen durch "UND" verknüpft. Die Wetterlage "Sunny" wird beispielsweise wie folgt berechnet:

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

3. Wetterstandortdaten

Diese Tabelle definiert die Wetterstandorte, die in der Tabelle mit den Kampagnendaten verwendet werden. Die Tabelle besteht nur aus zwei Spalten:

In dem Skript können Sie pro Wetterstandort mehrere Codes für die geografische Ausrichtung angeben. Das liegt daran, dass die Wetterstandorte nicht so genau sind wie die Ausrichtungsoptionen in AdWords. Daher müssen Sie möglicherweise einen einzelnen Wetterstandort mehreren geografischen Standorten zuordnen. Erstellen Sie dafür mehrere Zeilen mit demselben Wetterstandort, jedoch mit unterschiedlichem Code für die geografische Ausrichtung für jede Zeile.

In der Tabelle oben sind drei Wetterstandorte festgelegt:

  1. Boston, Massachusetts: Geocode 10108127
  2. Connecticut: Geocodes 1014778, 1014743 und 1014843, da es sich um mehrere Städte in Connecticut handelt
  3. Florida: Geocode 21142

Ausrichtung auf Umgebung

Kampagnenregeln, die Matching Geo Targets verwenden, können mit der Kennzeichnung TARGETING auf Zielregionen und Ausrichtungsziele in der Nähe oder auf beide Optionen angewendet werden. Bei der geografischen Ausrichtung werden Geocodes und Standort-IDs verwendet. Bei der Ausrichtung auf die Umgebung wird mithilfe der Haversine-Formel überprüft, ob die angegebenen Breiten- und Längengrade in der näheren Umgebung liegen.

Skriptlogik

Das Skript liest zunächst Regeln aus allen drei Tabellenblättern aus. Dann versucht es, jede Regel aus der Kampagnentabelle der Reihe nach auszuführen.

Das Skript prüft für jede ausgeführte Regel, ob die Kampagne auf den jeweiligen Standort ausgerichtet ist. Ist dies der Fall, ruft das Skript die aktuelle Gebotsanpassung ab. Dann wird die Wetterlage für diesen Standort mithilfe der OpenWeatherMap API abgerufen. Die Regeln für die Wetterlage werden ausgewertet, um zu prüfen, ob die Wetterlage für diesen Standort mit den Bedingungen in der Regel übereinstimmt. Wenn dies so ist und sich die neue Gebotsanpassung von der aktuellen Gebotsanpassung unterscheidet, wird sie vom Skript für diesen Standort geändert.

Es werden keine Änderungen vorgenommen, wenn die Wetterlage nicht übereinstimmt, die Gebotsanpassung sich nicht unterscheidet oder "Apply Modifier To" auf Matching Geo Targets festgelegt wurde, aber die Kampagne nicht auf die mit der Regel verknüpften Standorte ausgerichtet ist.

Einrichtung

  • Registrieren Sie sich auf http://openweathermap.org/appid für einen API-Schlüssel.
  • Erstellen Sie eine Kopie der Tabellenvorlage und bearbeiten Sie Ihre Kampagne und die Wetterregeln.
  • Erstellen Sie ein neues Skript mit dem Quellcode unten.
  • Passen Sie die Variablen OPEN_WEATHER_MAP_API_KEY, SPREADSHEET_URL und TARGETING im Skript an.
  • Planen Sie die Ausführung des Skripts nach Bedarf.

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

Allgemein

Dies ist ein allgemein gehaltenes Skript. Sie können es anpassen, um eine wetterabhängige Kampagnenverwaltung zu implementieren. Es gibt jedoch auch das Skript Bid-by-Weather, ein spezielles Skript mit dem Schwerpunkt auf wetterabhängiger Gebotsverwaltung.

Das Skript Bid-by-Weather zeigt, wie Sie die Gebote Ihrer Kampagnen je nach Wetterlage in den Zielregionen anpassen. Dieses Skript kann jedoch nur Gebote verwalten. Wenn Sie es so ändern möchten, dass andere Aufgaben übernommen werden, z. B. Pausieren einer Gruppe von Kampagnen, geht das nicht so einfach. In diesem Leitfaden wird ein allgemeineres Skript zur wetterabhängigen Verwaltung vorgestellt, mit dem Sie Aufgaben zur Kampagnenverwaltung basierend auf Wetterdaten durchführen können.

Wetterdaten abrufen

Das Skript ruft Wetterdaten von OpenWeatherMap-Servern ab. Sie können aktuelle Wetterdaten oder eine Wochenprognose abrufen. Ortsnamen lassen sich mit OpenWeatherMap flexibel übergeben:

// 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");

Informationen zum Datenformat

Die von der Methode getWeatherForLocation() zurückgegebenen Wetterdaten haben das folgende Format:

{
  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"
}

Die Properties haben die folgenden Bedeutungen:

Feld Details
name Kanonischer Name des Standorts, von OpenWeatherMap zurückgegeben
clouds Bewölkung als Prozentsatz
windspeed Windgeschwindigkeit in Meilen pro Sekunde
snow Schneefall in den letzten 3 Stunden, mm
rain Niederschlag in den letzten 3 Stunden, mm
summary Eine Zusammenfassung des Wetters; gültige Werte siehe http://openweathermap.org/weather-conditions
description Eine Beschreibung des Wetters; gültige Werte siehe http://openweathermap.org/weather-conditions
id Von OpenWeathermap zurückgegebene ID für die Wetterlage; gültige Werte siehe http://openweathermap.org/weather-conditions
temperature Temperatur in Kelvin

Die von der Methode getWeatherForecastForLocation() zurückgegebene Wettervorhersage hat ein ähnliches Format:

{
  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": {
      ...
    }
  }
}

Die Eigenschaften im Prognosefeed haben die gleiche Bedeutung wie im Wetterfeed. Die zusätzliche Eigenschaft "forecast" (Prognose) ist eine Zuordnungstabelle. Ihr Schlüssel ist ein Datum im Format yyyyMMdd und ihr Wert die Wettervorhersagedaten für diesen Tag. Standardmäßig werden Wettervorhersagen für sieben Tage zurückgegeben. Im Skript wird das von der OpenWeatherMap API zurückgegebene JSON-Datenformat vereinfacht.

Kampagnen verwalten

Im Folgenden finden Sie einige Beispiele, wie Sie Ihre Kampagnen auf Basis dieser Wetterdaten verwalten können.

Aktuelles Wetter

Beispiel 1: Die Wetterübersicht von OpenWeathermap verwenden

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

Beispiel 2: Speziellere Wetterparameter auswerten

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

Wettervorhersage

Beispiel 1: Die Wetterübersicht von OpenWeathermap verwenden

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

Beispiel 2: Speziellere Wetterparameter auswerten

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

Einrichtung

  • Registrieren Sie sich auf http://openweathermap.org/appid für einen API-Schlüssel.
  • Erstellen Sie ein neues Skript mit dem Quellcode unten.
  • Passen Sie OPEN_WEATHER_MAP_API_KEY an.
  • Entfernen Sie je nachdem, ob Sie dieses Skript mit einem Kundenkonto oder einem Verwaltungskonto ausführen möchten, die Kennzeichnung des entsprechenden Codeblocks als Kommentar in der Methode main().
  • Entfernen Sie die Kennzeichnung des entsprechenden Codeblocks als Kommentar in der Methode processSingleAccount(), je nachdem, ob Sie Daten zum aktuellen Wetter oder Wettervorhersagedaten verwenden möchten.
  • Ersetzen Sie den Inhalt der Methode manageCampaignsBasedOnCurrentWeather() bzw. manageCampaignsBasedOnWeatherForecast() durch eigenen Code zur Kampagnenverwaltung.
  • Planen Sie die Ausführung des Skripts nach Bedarf.

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

Feedback geben zu...

AdWords scripts
AdWords scripts