Bouw en onderhoud Google Shopping campagnes

Gebruik dit script om Google Shopping campagnes op te bouwen en te onderhouden in je account.

Start nu!
Bouw en onderhoud Google Shopping Campagnes
Bouw en Get started!

Bij grote webshops in het onmogelijk om alle Google Shopping campagnes handmatig up-to-date te houden. Vaak komen er nieuwe producten bij of verdwijnen producten uit het assortiment. Om je Google Shopping campagnes dan bij te houden, zullen deze producten toegevoegd of verwijderd moeten worden. Dat kan makkelijker.

Er zijn ook verschillende externe tools om Google Shopping campagnes automatisch aan te maken. Maar je kan dit ook met scripts doen. Dit script maakt verbinding met je Google Merchant Center en plaatst daarna alle producten in de juiste campagne(s) en advertentiegroep. Het script maakt de campagnes niet aan, dat zal je wel zelf moeten doen. Wanneer je gebruik wilt maken van prioriteit tussen campagnes, bijvoorbeeld voor een splitsing in brand en non-brand, zal dat met externe tools ook handmatig moeten. De advertentiegroepen worden wel aangemaakt door het script.

Actueel aanbod in Google Shopping

Het script voert twee taken uit:

  1. Productgroepen verwijderen die niet meer Google Merchant Center staan.
  2. Productgroepen toevoegen aan de juiste advertentiegroepen wanneer deze nog niet in Google Shopping staan.

De selectie van de juiste campagne en advertentiegroep kan op basis van waarden uit Google Merchant Center. Daarnaast kan je gebruik maken van een statische waarde om de juiste campagne te selecteren. In het script dat hieronder staat, wordt gebruik gemaakt van beide. Op basis van CAMPAIGN_SELECTOR worden alle Google Shopping campagnes geselecteerd die 'Bestsellers' in de naam hebben staan. Omdat CAMPAIGNNAME_FIELD ook ingesteld is, moet de campagne daarnaast ook de merknaam van het product bevatten. Als voorbeeld: voor een Apple Macbook wordt een campagne geselecteerd die 'Shopping - Bestsellers - Apple' heet, omdat deze zowel 'Bestsellers' als de merknaam van het product bevat.

Binnen deze campagne wordt daarna de advertentiegroep bevat die de inhoud van Custom Label 0 in de naam heeft staan. Als deze advertentiegroep nog niet bestaat, wordt deze automatisch aangemaakt.

Instellingen

  • LOG: Geef aan of het script de tussenstappen moet rapporteren, door de waarde aan te passen naar 'true'.
  • MERCHANT_ID: Het ID van het Merchant Center account. De gebruiker die het script draait, moet ook als gebruiker zijn toegevoegd aan Merchant Center. Je moet ook de geavanceerde API 'Shopping Content' aanzetten in het script (rechtsboven het script-veld).
  • DEFAULT_BID: De Max. CPC die ingevuld wordt voor elk product en advertentiegroep.
  • TARGET_COUNTRY: Selecteer alleen producten uit GMC voor een specifiek land. Leeg laten als je dit niet wilt gebruiken.
  • CAMPAIGN_SELECTOR: De campagnenaam die selecteert wordt voor elk product uit GMC moet deze waarde bevatten. Leeg laten als je dit niet wilt gebruiken.
  • CAMPAIGNNAME_FIELD: Gebruik een veld uit GMC om een campagne te selecteren. De campagne die voor een product geselecteerd wordt, moet de inhoud van dit veld in de naam hebben staan. Een overzicht van alle beschikbare velden: https://developers.google.com/shopping-content/reference/rest/v2/products
  • ADGROUPNAME_FIELD: Selecteer de juiste advertentiegroep op basis van een waarde uit GMC. Per product wordt de advertentiegroep geselecteerd die de inhoud van dit veld bevat.

Frequentie: als je meer dan 500 producten hebt, stel het script de eerste keer voor elk uur in. Als alle producten in de campagnes staan, verander dit dan naar dagelijks. Zorg ervoor dat je een tijd kiest nadat de productfeed in Merchant Center is bijgewerkt.

The script
// Copyright 2020. Increase BV. All Rights Reserved.
//
// Created By: Tibbe van Asten
// for Increase B.V.
//
// Created 15-04-2019
// Last update: 04-08-2020
//
// ABOUT THE SCRIPT
// All products in Google Merchant Center are checked for existance
// in the Google Shopping campaigns. Based on your input, products
// will be put in the right campaign and adgroup.
// The script also checks all active productgroups in your campaigns
// and removes productgroups that are not in GMC anymore.
//
////////////////////////////////////////////////////////////////////

var config = {

  LOG : true,

  // Connect Merchant Center. Add user to GMC that runs this script.
  // Also enable Advanced API 'Shopping content'.
  MERCHANT_ID : "123456789",
  DEFAULT_BID : 0.3,

  TARGET_COUNTRY : "NL",

  // To select a (set of) Shopping campaign(s), all names must contain the following
  // Leave empty when you only want to use a field from GMC to select a campaign.
  CAMPAIGN_SELECTOR : "Bestsellers",

  // To specify the corresponding campaign and adgroup for each product,
  // we'll use fields from GMC. Define which field contains (part of) the
  // campaign- and adgroupname.
  // Available fieldnames: https://developers.google.com/shopping-content/reference/rest/v2/products

  // Leave empty when you only want to use the CAMPAIGN_SELECTOR to select a campaign.
  CAMPAIGNNAME_FIELD : "brand",

  // use productType, customLabel0 (or 1,2,3,4) or brand
  ADGROUPNAME_FIELD : "customLabel0"

}

////////////////////////////////////////////////////////////////////

function main(){

  // Connect Merchant Center and collect active brands and products
  var merchantCenter = connectMerchant();
  var products = merchantCenter["products"];
  var productsIds = merchantCenter["productsIds"];

  var productGroups = activeProductGroups();
  var activeProducts = productGroups["activeProducts"];
  var activeProductsIds = productGroups["activeProductsIds"];

  var activeSubcategories = activeAdGroups();
  var addProducts = {};
  var removeProducts = [];

  // Check which products need to be added
  // Loop through all products from Merchant Center
  for (var i = 0; i < products.length; i++) {

    // If product is in stock and not in any campaign, we'll add it
    if(products[i]["availability"] == "in stock" && activeProductsIds.indexOf(products[i]["offerId"]) < 0 && activeProductsIds.indexOf(products[i]["offerId"].toLowerCase())  < 0){

      // Make sure addProducts isn't empty
      if (addProducts[products[i]["offerId"]] == null) {
        addProducts[products[i]["offerId"]] = [];
      }
      addProducts[products[i]["offerId"]].push([products[i]["offerId"], products[i][config.CAMPAIGNNAME_FIELD], products[i][config.ADGROUPNAME_FIELD]]);
    }

  } // merchantCenterIterator

  if(config.LOG === true){
    Logger.log("Found " + Object.keys(addProducts).length + " to be added");
    Logger.log(" ");
  }

  // Check which products need to be removed
  // Loop through all products from the campaigns
  for (var i = 0; i < activeProducts.length; i++) {

    // If product is not in Merchant Center, we'll remove it
    if(productsIds.indexOf(activeProducts[i].getValue()) < 0 && productsIds.indexOf(activeProducts[i].getValue().toUpperCase()) < 0 && activeProducts[i].getDimension() == "ITEM_ID"){
        if(config.LOG === true){
          Logger.log("Removed " + activeProducts[i].getValue());
        }

      activeProducts[i].remove();

    }

  } // activeProductsIterator

  // Add products
  for (var key in addProducts) {

    if(typeof addProducts[key][0][2] == "undefined") continue;
    var shoppingAdGroups = AdsApp.shoppingAdGroups().withCondition("CampaignName CONTAINS '"+config.CAMPAIGN_SELECTOR+"'");

      // If a field is defined to select the campaignname
      if(config.CAMPAIGNNAME_FIELD != ""){
        shoppingAdGroups = shoppingAdGroups.withCondition("CampaignName CONTAINS '"+addProducts[key][0][1]+"'");
      }

    shoppingAdGroups = shoppingAdGroups.withCondition("Name CONTAINS '"+addProducts[key][0][2]+"'").get();

    if(!shoppingAdGroups.hasNext()){
      var campaign = AdsApp.shoppingCampaigns().withCondition("Name CONTAINS '"+config.CAMPAIGN_SELECTOR+"'");

        // If a field is defined to select the campaignname
        if(config.CAMPAIGNNAME_FIELD != ""){
          campaign = campaign.withCondition("Name CONTAINS '"+addProducts[key][0][1]+"'");
        }

      campaign = campaign.get().next();

      // Adding adgroup to Shopping campaign
      var shoppingAdGroup = campaign
        .newAdGroupBuilder()
        .withName(addProducts[key][0][2])
        .withCpc(config.DEFAULT_BID)
        .withStatus("ENABLED")
        .build()
        .getResult();

      if(config.LOG === true){
        Logger.log(shoppingAdGroup.getName() + " added to " + campaign.getName());
      }

      // Adding an ad to the new adgroup
      var ad = shoppingAdGroup
        .newAdBuilder()
        .build()
        .getResult();

      // Creating the root productgroup 'All Products' and select it
      shoppingAdGroup.createRootProductGroup();
      var root = shoppingAdGroup.rootProductGroup();

      // Creating a productgroup for this product
        if(config.ADGROUPNAME_FIELD == "productType"){
          var child = root.newChild().productTypeBuilder()
            .withBid(config.DEFAULT_BID)
            .withValue(addProducts[key][0][2])
            .build();
        }

        if(config.ADGROUPNAME_FIELD.indexOf("customLabel") > -1){
          var type = "CUSTOM_LABEL_" + config.ADGROUPNAME_FIELD.slice(-1);
          var child = root.newChild().customLabelBuilder()
            .withBid(config.DEFAULT_BID)
            .withType(type)
            .withValue(addProducts[key][0][2])
            .build();
        }

        if(config.ADGROUPNAME_FIELD == "brand"){
          var child = root.newChild().brandBuilder()
            .withBid(config.DEFAULT_BID)
            .withValue(addProducts[key][0][2])
            .build();
        }

      // Putting item right under productType
      child.getResult().newChild().itemIdBuilder()
        .withBid(config.DEFAULT_BID)
        .withValue(addProducts[key][0][0])
        .build();

      // Making sure all other products in this adgroup are excluded
      var children = root.children().get();
      while (children.hasNext()) {
        var child = children.next();
        if (child.isOtherCase()) {
          child.exclude();
        }
      } // childrenIterator
    }

    // If adgroup already exists, we'll add the product
    while(shoppingAdGroups.hasNext()){
      var shoppingAdGroup = shoppingAdGroups.next();

      var type = shoppingAdGroup.rootProductGroup().children().withCondition("ProductGroup CONTAINS '"+addProducts[key][0][2].toLowerCase()+"'").get().next().asProductType();

      // Creating a productgroup for this product
      type.newChild().itemIdBuilder()
        .withBid(config.DEFAULT_BID)
        .withValue(addProducts[key][0][0])
        .build();

        if(config.LOG === true){
          Logger.log("Added " + addProducts[key][0][0] + " to " + shoppingAdGroup.getName());
        }
    } // shoppingAdGroupsIterator

  } // addProductsIterator

}

////////////////////////////////////////////////////////////////////

function connectMerchant(){

  	if(config.MERCHANT == "123456789"){
      throw Error("Change the Merchant ID in the settings");
    }

  var pageToken;
  var pageNum = 1;
  var maxResults = 250;
  var products = [];
  var productsIds = [];

  do {
    var productList = ShoppingContent.Products.list(config.MERCHANT_ID, {
      pageToken: pageToken,
      maxResults: maxResults
    });

    if (productList.resources) {
      for (var i = 0; i < productList.resources.length; i++) {

        // If targetcountry is set, only corresponding products will be selected
        if(config.TARGET_COUNTRY && productList.resources[i]["targetCountry"] == config.TARGET_COUNTRY){
          products.push(productList.resources[i]);
          productsIds.push(productList.resources[i]["offerId"]);
        } else {
          products.push(productList.resources[i]);
          productsIds.push(productList.resources[i]["offerId"]);
        }

      }
    }

    pageToken = productList.nextPageToken;
    pageNum++;
  } while (pageToken);

  return {products : products, productsIds : productsIds};

} // function connectMerchant

////////////////////////////////////////////////////////////////////

function activeProductGroups(){

  var activeProducts = [];
  var activeProductsIds = [];

  // We collect all active productGroups
  var productGroupIterator = AdsApp
    .productGroups()
    .withCondition("CampaignName CONTAINS '"+config.CAMPAIGN_SELECTOR+"'")
    .withCondition("AdGroupStatus != REMOVED")
    .withCondition("ProductGroup DOES_NOT_CONTAIN ' OtherCase'")
    .get();

  while(productGroupIterator.hasNext()){
    var productGroup = productGroupIterator.next();

    // Only add a product if it's an active, actual product
    if(productGroup.isOtherCase() === false && productGroup.getValue() != null){
      activeProducts.push(productGroup);
      activeProductsIds.push(productGroup.getValue());
    }
  } // productGroupIterator

  return {activeProducts : activeProducts, activeProductsIds : activeProductsIds};

} // activeProducts

////////////////////////////////////////////////////////////////////

function activeAdGroups(){

  var activeSubcategories = [];

  var adGroupIterator = AdsApp
    .shoppingAdGroups()
    .withCondition("CampaignName = '"+config.CAMPAIGN_SELECTOR+"'")
    .withCondition("Status != REMOVED")
    .get()

  while(adGroupIterator.hasNext()){
    var adGroup = adGroupIterator.next();

    // Add adgroupname as subcategory
    if(activeSubcategories.indexOf(adGroup.getName()) <0){
      activeSubcategories.push(adGroup.getName());
    }

  } // adGroupIterator

  return activeSubcategories;

} // activeAdGroups
Show whole script!
The Experts
Tibbe van Asten Head of PPC @ Increase
Nils Rooijmans Water Cooler Topics
Martijn Kraan Freelance PPC Specialist
Bas Baudoin Teamlead SEA @ Happy Leads
How about you? JOIN US!
Sharing Knowledge
Caring

Kennis delen

Adsscripts.com staat voor het delen van kennis. In de huidige markt houden SEA-specialisten de kennis en ervaring graag voor zich. Wij zijn er van overtuigd dat het delen van kennis ervoor kan zorgen dat iedereen beter wordt in haar of zijn werk. Daarom lopen wij hier graag in voorop, door onze kennis over scripts te delen met iedereen.

Wil jij ook graag een bijdrage leveren? Wij staan open voor nieuwe ideeën en feedback op alles wat je op Adsscripts.com vindt.

Neem contact op

Training &
Workshop
Neem contact op!
Adsscripts Training & Workshop