Ne perdez plus votre budget sur de mauvais mots clés

Utiliser les outils avancés de Google ads (les scripts) pour sauver son budget

🚨 Le pire ennemi d’un trafic manager sur Google ads peut être le temps d’analyse. Mettez 100 mots-clés, 10 colonnes d’analyses par mots-clés (coût, impression, conversion, etc…) et saupoudrez cela de l’UX assez particulière de Google ads… et c’est le drame : comme tout le monde, le trafic manager ne va analyser que le top 10 dépenses… la partie émergée de l’iceberg.

Il est simple de passer à côté de mots-clés ne faisant pas de conversion, des taux de clics trop bas, et j’en passe. Cela peut amener à une perte de budget légère, mais récurrente. À l’échelle du trimestre ce n’est pas négligeable.

🤖 Si l’automatisation ne résout pas tous nos problèmes, cela peut néanmoins aider dans ce cadre.

On recommande donc fortement d’utiliser des Scripts, qui vont faire ce travail d’analyse à votre place, et afficher sur l’interface tous les soucis !

Comment ? On mettant en place des « labels », qu’il suffira de classer ensuite pour prendre action.

Exemple de labels sur des mots-clés avec 1 000 impressions MAIS un CTR inférieur à 5% ! : urgence à retravailler les annonces…

👉 La bonne nouvelle ? Vous pouvez vous-même définir votre cadre d’alerte.

On aime bien chez webloom être alerté sur l’interface Google ads automatiquement quand :

  • Les mots-clés font moins de 5 conversions
  • Les mots-clés font moins de 5% de CTR mais ont bcp d’impressions
  • Les mots-clés font moins de 5/10 de quality score mais ont plus de 30 conversions
  • ect…

C’est ensuite à vous de travailler pour résoudre le problème, afin de supprimer le label de votre interface 🙂

Mettre en pause, réecrire les annonces, ajouter des extensions, vérifier les termes de recherches…

🔍 Comment faire ?

  • Outils et paramètre
  • Onglet Scripts
  • Petit bouton +
  • Cliquez sur « Accepter »

  • Copier-coller le script en fin d’article

  • Modifier le lien du Google Sheet dans le script en mettant le vôtre (pas obligatoire)
  • Faire une Preview et autoriser de nouveau le script.
  • Cliquez sur Run ! (même si le script affiche des erreurs, les labels seront bien posés)
  • De retour dans la liste des scripts, passez la fréquence sur 1 semaine.

Le script est défini sur une analyse de 7 jours glissants.

📝 Voici le blog officiel Google sur le sujet :

https://developers.google.com/google-ads/scripts/docs/solutions/labels

Ou voir vos labels ?

Recharger la page Google ads, et aller voir votre tableau mots-clés. Il est possible que vous deviez ajouter une colonne label :

Une fois vos actions correctives mise en place, il suffit de cliquer sur un label et de le supprimer.

Tous les autres labels disparaîtront aussi.

Le script anti perte du budget à copier-coller

// 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 Keyword Labeler
 *
 * @overview The Keyword Labeler script labels keywords based on rules that
 *     you define. For example, you can create a rule to label keywords that
 *     are underperforming. Later, you can filter for this label in Google Ads
 *     to decide whether to pause or remove those keywords. Rules don't have
 *     to be based solely on a keyword's performance. They can also be based
 *     on properties of a keyword such as its text or match type. For example,
 *     you could define "branded" keywords as those containing proper nouns
 *     associated with your brand, then label those keywords based on
 *     different performance thresholds versus "non-branded" keywords.
 *     Finally, the script sends an email linking to a spreadsheet when new
 *     keywords have been labeled. See
 *     https://developers.google.com/google-ads/scripts/docs/solutions/labels
 *     for more details.
 *
 * @author Google Ads Scripts Team [[email protected]]
 *
 * @version 2.0
 *
 * @changelog
 * - version 2.0
 *   - Updated to use new Google Ads scripts features.
 * - version 1.1.2
 *   - Added validation for external spreadsheet setup.
 * - version 1.1.1
 *   - Improvements to time zone handling.
 * - version 1.1
 *   - Modified to allow generic rules and labeling.
 * - version 1.0
 *   - Released initial version.
 */

const CONFIG = {
  // URL of the spreadsheet template.
  // This should be a copy of https://goo.gl/opviBb.
  SPREADSHEET_URL: 'https://docs.google.com/spreadsheets/d/1Kd1pvv8JK7wDKBM4TAw4Sz7OjEfRLrpa8prwbi5YOd8/edit?usp=sharing',

  // Array of addresses to be alerted via email if labels are applied.
  RECIPIENT_EMAILS: [
    '[email protected]'
  ],

  // Selector conditions to apply for all rules.
  GLOBAL_CONDITIONS: [
    'campaign.status = ENABLED',
    'ad_group.status = ENABLED',
    'ad_group_criterion.status = ENABLED'
  ],

  // Default date range over which statistics fields are retrieved.
  // Used when fetching keywords if a rule doesn't specify a date range.
  DEFAULT_DATE_RANGE: 'LAST_7_DAYS'
};

/**
 * Defines the rules by which keywords will be labeled.
 * The labelName field is required. Other fields may be null.
 * @type {Array.<{
 *     conditions: Array.<string>,
 *     dateRange: string,
 *     filter: function(Object): boolean,
 *     labelName: string,
 *   }>
 * }
 */
const RULES = [
  {
    conditions: [
      'metrics.ctr < 0.04',
      'metrics.impressions > 1000',
    ],
    labelName: '👀 CTR'
  },

  {
    conditions: [
      'metrics.conversions < 2',
    ],
    labelName: '🚨 CONV.'
  }
];

function main() {
  validateEmailAddresses();
  const results = processAccount();
  processResults(results);
}

/**
 * Processes the rules on the current account.
 *
 * @return {Array.<Object>} An array of changes made, each having
 *     a customerId, campaign name, ad group name, label name,
 *     and keyword text that the label was applied to.
 */
function processAccount() {
  ensureAccountLabels();
  const changes = applyLabels();

  return changes;
}

/**
 * Processes the results of the script.
 *
 * @param {Array.<Object>} changes An array of changes made, each having
 *     a customerId, campaign name, ad group name, label name,
 *     and keyword text that the label was applied to.
 */
function processResults(changes) {
  if (changes.length > 0) {
    const spreadsheetUrl = saveToSpreadsheet(changes, CONFIG.RECIPIENT_EMAILS);
    sendEmail(spreadsheetUrl, CONFIG.RECIPIENT_EMAILS);
  } else {
    console.log('No labels were applied.');
  }
}

/**
 * Retrieves the names of all labels in the account.
 *
 * @return {Array.<string>} An array of label names.
 */
function getAccountLabelNames() {
  const labelNames = [];

  for (const label of AdsApp.labels()) {
    labelNames.push(label.getName());
  }

  return labelNames;
}

/**
 * Checks that the account has a label for each rule and
 * creates the rule's label if it does not already exist.
 * Throws an exception if a rule does not have a labelName.
 */
function ensureAccountLabels() {
  const labelNames = getAccountLabelNames();

  for (var i = 0; i < RULES.length; i++) {
    const labelName = RULES[i].labelName;

    if (!labelName) {
      throw `Missing labelName for rule #${i}`;
    }

    if (labelNames.indexOf(labelName) == -1) {
      AdsApp.createLabel(labelName);
      labelNames.push(labelName);
    }
  }
}

/**
 * Retrieves the keywords in an account satisfying a rule
 * and that do not already have the rule's label.
 *
 * @param {Object} rule An element of the RULES array.
 * @return {Array.<Object>} An array of keywords.
 */
function getKeywordsForRule(rule) {
  let selector = AdsApp.keywords();
  let includedLabel;

  // Add global conditions.
  for (const globalCondition of CONFIG.GLOBAL_CONDITIONS) {
    selector = selector.withCondition(globalCondition);
  }

  // Add selector conditions for this rule.
  if (rule.conditions) {
    for (const ruleCondition of rule.conditions) {
      selector = selector.withCondition(ruleCondition);
    }
  }

  if(rule.labelName){
    includedLabel = AdsApp.labels()
        .withCondition(`label.name = '${rule.labelName}'`)
        .get();
    if(includedLabel.hasNext()){
      includedLabel=includedLabel.next();
    }
  }

  // Exclude keywords that already have the label.
  selector.withCondition(`LabelNames CONTAINS_NONE ["${rule.labelName}"]`);

  // Add a date range.
  selector = selector.forDateRange(rule.dateRange || CONFIG.DEFAULT_DATE_RANGE);

  // Get the keywords.
  const iterator = selector.get();
  const keywords = [];

  // Check filter conditions for this rule.
  for (const keyword of iterator) {
    if (!rule.filter || rule.filter(keyword)) {
      keywords.push(keyword);
    }
  }

  return keywords;
}

/**
 * For each rule, determines the keywords matching the rule and which
 * need to have a label newly applied, and applies it.
 *
 * @return {Array.<Object>} An array of changes made, each having
 *     a customerId, campaign name, ad group name, label name,
 *     and keyword text that the label was applied to.
 */
function applyLabels() {
  const changes = [];
  const customerId = AdsApp.currentAccount().getCustomerId();

  for (const rule of RULES) {
    const keywords = getKeywordsForRule(rule);
    const labelName = rule.labelName;

    for (const keyword of keywords) {

      keyword.applyLabel(labelName);

      changes.push({
        customerId: customerId,
        campaignName: keyword.getCampaign().getName(),
        adGroupName: keyword.getAdGroup().getName(),
        labelName: labelName,
        keywordText: keyword.getText(),
      });
    }
  }

  return changes;
}

/**
 * Outputs a list of applied labels to a new spreadsheet and gives editor access
 * to a list of provided emails.
 *
 * @param {Array.<Object>} changes An array of changes made, each having
 *     a customerId, campaign name, ad group name, label name,
 *     and keyword text that the label was applied to.
 * @param {Array.<Object>} emails An array of email addresses.
 * @return {string} The URL of the spreadsheet.
 */
function saveToSpreadsheet(changes, emails) {
  const template = validateAndGetSpreadsheet(CONFIG.SPREADSHEET_URL);
  const spreadsheet = template.copy('Keyword Labels Applied');

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

  console.log(`Saving changes to spreadsheet at ${spreadsheet.getUrl()}`);

  const headers = spreadsheet.getRangeByName('Headers');
  const outputRange = headers.offset(1, 0, changes.length);

  const outputValues = [];
  for (const change of changes) {
    outputValues.push([
      change.customerId,
      change.campaignName,
      change.adGroupName,
      change.keywordText,
      change.labelName
    ]);
  }
  outputRange.setValues(outputValues);

  spreadsheet.getRangeByName('RunDate').setValue(new Date());

  for (const email of emails) {
    spreadsheet.addEditor(email);
  }

  return spreadsheet.getUrl();
}

/**
 * Sends an email to a list of email addresses with a link to a spreadsheet.
 *
 * @param {string} spreadsheetUrl The URL of the spreadsheet.
 * @param {Array.<Object>} emails An array of email addresses.
 */
function sendEmail(spreadsheetUrl, emails) {
  MailApp.sendEmail(emails.join(','), 'Keywords Newly Labeled',
      `Keywords have been newly labeled in your` +
      `Google Ads account(s). See ` +
      `${spreadsheetUrl} for details.`);
}

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

/**
 * Validates the provided spreadsheet URL and email address
 * to make sure that they're 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 or email hasn't been set
 */
function validateAndGetSpreadsheet(spreadsheeturl) {
  if (spreadsheeturl == 'YOUR_SPREADSHEET_URL') {
    throw new Error('Please specify a valid Spreadsheet URL. You can find' +
        ' a link to a template in the associated guide for this script.');
  }
  return SpreadsheetApp.openByUrl(spreadsheeturl);
}

/**
 * Validates the provided email address to make sure it's not the default.
 * Throws a descriptive error message if validation fails.
 *
 * @throws {Error} If the list of email addresses is still the default
 */
function validateEmailAddresses() {
  if (CONFIG.RECIPIENT_EMAILS &&
      CONFIG.RECIPIENT_EMAILS[0] == 'YOUR_EMAIL_HERE') {
    throw new Error('Please specify a valid email address.');
  }
}

Accélérer sa croissance grâce à Google ?

Remplissez notre questionnaire en 2 minutes pour être contacté par un expert webloom. Nous analysons gratuitement votre marché pour identifier les opportunités SEO / Google ads.