Maintenir une liste d'éléments à exclure mots clés ou à exclure d'emplacements est une tâche courante Gestion des campagnes Google Ads Cette liste sert généralement de filtre mots clés ou emplacements qui génèrent du trafic indésirable vers votre site Web.
Google Ads accepte les listes de critères à exclure qui peuvent être partagées avec plusieurs dans un seul compte. En revanche, si vous appliquez la liste à plusieurs campagnes, dans un compte de grande taille et la synchronisation de la liste au fil du temps peut s'avérer difficile. Le script de liste à exclure commun simplifie cette tâche en vous permettant de gérer le les critères négatifs à l’aide d’une feuille de calcul.
Fonctionnement
Le script lit les critères négatifs d'une feuille de calcul Google Sheets. Il crée un liste de critères à exclure partagés dans le compte Google Ads et synchronise cette liste avec les critères de la feuille de calcul. Des listes distinctes sont créées pour les mots clés et les emplacements.
Le script s'assure ensuite que la liste des critères à exclure est appliquée à toutes les campagnes du compte. Si nécessaire, vous pouvez limiter la liste des campagnes par : Spécifiez un libellé dans la feuille de calcul de configuration pour filtrer les données à inclure. lors du traitement des campagnes.
Le script peut éventuellement envoyer un e-mail résumant ses actions à un e-mail spécifiée dans la feuille de calcul de configuration.
Configuration
Le script utilise une feuille de calcul pour la configuration. La configuration suivante sont acceptés:
- Le script traite toutes les campagnes
ENABLED
etPAUSED
d'un compte. par défaut. Pour limiter la liste des campagnes traitées,- Créez un libellé dans chaque compte à traiter.
- Appliquez ce libellé à la liste des campagnes à traiter.
- Spécifiez cette étiquette dans la cellule C3 de la feuille de calcul de configuration.
- Indiquez une adresse e-mail dans la cellule C6 pour recevoir un e-mail de synthèse une fois que le s'exécute.
- Le script crée des listes distinctes de critères à exclure partagés pour les mots clés. d'emplacements dans les comptes qu'il traite. Indiquez le nom de l'instance listes de critères négatifs dans la feuille de calcul de configuration, cellules C4 et C5.
- Omettez tout préfixe de protocole (
http://
ouhttps://
) dans les URL d'emplacement de dans votre liste d'emplacements. - Assurez-vous que les URL d'emplacement ne se terminent pas par une barre oblique (
/
). - Assurez-vous que toutes les URL d'emplacement sont en minuscules dans la feuille de calcul.
Planification
Planifiez l'exécution du script une fois par jour ou toutes les heures.
Configuration
Cliquez sur le bouton ci-dessous pour créer le script basé sur une feuille de calcul dans votre Google Ads.
Cliquez sur le bouton ci-dessous pour faire une copie du modèle de feuille de calcul.
Mettez à jour
spreadsheet_url
dans votre script.
Code source
// 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 Common Negative List Script
*
* @overview The Common Negative List script applies negative keywords and
* placements from a spreadsheet to multiple campaigns in your account using
* shared keyword and placement lists. See
* https://developers.google.com/google-ads/scripts/docs/solutions/common-negative-list
* for more details.
*
* @author Google Ads Scripts Team [adwords-scripts@googlegroups.com]
*
* @version 2.2
*
* @changelog
* - version 2.2
* - Fixed an issue where the match type of keywords in the negative list was
* ignored.
* - version 2.1
* - Split into info, config, and code.
* - version 2.0
* - Updated to use new Google Ads scripts features.
* - version 1.0.2
* - Added validation for external spreadsheet setup.
* - version 1.0.1
* - Improvements to time zone handling.
* - version 1.0
* - Released initial version.
*/
/**
* Configuration to be used for the Common Negative List script.
*/
CONFIG = {
/**
* The URL of the tracking spreadsheet. This should be a copy of
* https://goo.gl/PZGKVn
*/
'spreadsheet_url': 'INSERT_SPREADSHEET_URL_HERE'
};
const SPREADSHEET_URL = CONFIG.spreadsheet_url;
/**
* Keep track of the spreadsheet names for various criteria types, as well as
* the criteria type being processed.
*/
const CriteriaType = {
KEYWORDS: 'Keywords',
PLACEMENTS: 'Placements'
};
/**
* Create a shared negative criteria list in the Google Ads account and
* syncs the list with the criteria from the spreadsheet.
*/
function main() {
let emailParams = {
// Number of placements that were synced.
PlacementCount: 0,
// Number of keywords that were synced.
KeywordCount: 0,
// Number of campaigns that were synced.
CampaignCount: 0,
// Status of processing this account - OK / ERROR.
Status: ''
};
try {
const syncSummary = syncCommonLists();
emailParams.PlacementCount = syncSummary.PlacementCount;
emailParams.KeywordCount = syncSummary.KeywordCount;
emailParams.CampaignCount = syncSummary.CampaignCount;
emailParams.Status = 'OK';
} catch (err) {
emailParams.Status = 'ERROR';
}
const config = readConfig();
const spreadsheet = validateAndGetSpreadsheet(SPREADSHEET_URL);
// Make sure the spreadsheet is using the account's timezone.
spreadsheet.setSpreadsheetTimeZone(AdsApp.currentAccount().getTimeZone());
spreadsheet.getRangeByName('LastRun').setValue(new Date());
spreadsheet.getRangeByName('CustomerId').setValue(
AdsApp.currentAccount().getCustomerId());
sendEmail(config, emailParams);
}
/**
* Sends a summary email about the changes that this script made.
*
* @param {Object} config The configuration object.
* @param {Object} emailParams Contains details required to create the email
* body.
*/
function sendEmail(config, emailParams) {
const html = [];
let summary = '';
if (emailParams.Status == 'OK') {
summary = `The Common Negative List script successfully processed ` +
`Customer ID: ${AdsApp.currentAccount().getCustomerId()}` +
` and synced a total of ${emailParams.KeywordCount}` +
` keywords and ${emailParams.PlacementCount} placements.`;
} else {
summary = `The Common Negative List script failed to process ` +
`Customer ID: ${AdsApp.currentAccount().getCustomerId()}` +
` and synced a total of ${emailParams.KeywordCount}` +
` keywords and ${emailParams.PlacementCount} placements.`;
}
html.push('<html>',
'<head></head>',
'<body>',
'<table style="font-family:Arial,Helvetica; ' +
'border-collapse:collapse;font-size:10pt; ' +
'color:#444444; border: solid 1px #dddddd;" ' +
'width="600" cellpadding=20>',
'<tr>',
'<td>',
'<p>Hello,</p>',
'<p>' + summary + '</p>',
'<p>Cheers<br />Google Ads Scripts Team</p>',
'</td>',
'</tr>',
'</table>',
'</body>',
'</html>'
);
if (config.email != '') {
MailApp.sendEmail({
to: config.email,
subject: 'Common Negative List Script',
htmlBody: html.join('\n')
});
}
}
/**
* Synchronizes the negative criteria list in an account with the common list
* in the user spreadsheet.
*
* @return {Object} A summary of the number of keywords and placements synced,
* and the number of campaigns to which these lists apply.
*/
function syncCommonLists() {
const config = readConfig();
let syncedCampaignCount = 0;
const keywordListDetails = syncCriteriaInNegativeList(config,
CriteriaType.KEYWORDS);
syncedCampaignCount = syncCampaignList(config, keywordListDetails.SharedList,
CriteriaType.KEYWORDS);
const placementListDetails = syncCriteriaInNegativeList(config,
CriteriaType.PLACEMENTS);
syncCampaignList(config, placementListDetails.SharedList,
CriteriaType.PLACEMENTS);
return {
'CampaignCount': syncedCampaignCount,
'PlacementCount': placementListDetails.CriteriaCount,
'KeywordCount': keywordListDetails.CriteriaCount
};
}
/**
* Synchronizes the list of campaigns covered by a negative list against the
* desired list of campaigns to be covered by the common list.
*
* @param {Object} config The configuration object.
* @param {AdsApp.NegativeKeywordList|AdsApp.ExcludedPlacementList}
* sharedList The shared negative criterion list to be synced against the
* common list.
* @param {String} criteriaType The criteria type for the shared negative list.
*
* @return {Number} The number of campaigns synced.
*/
function syncCampaignList(config, sharedList, criteriaType) {
const campaignIds = getLabelledCampaigns(config.label);
let totalCampaigns = Object.keys(campaignIds).length;
const listedCampaigns = sharedList.campaigns().get();
const campaignsToRemove = [];
for (const listedCampaign of listedCampaigns) {
if (listedCampaign.getId() in campaignIds) {
delete campaignIds[listedCampaign.getId()];
} else {
campaignsToRemove.push(listedCampaign);
}
}
// Anything left over in campaignIds starts a new list.
const campaignsToAdd = AdsApp.campaigns().withIds(
Object.keys(campaignIds)).get();
for (const campaignToAdd of campaignsToAdd) {
if (criteriaType == CriteriaType.KEYWORDS) {
campaignToAdd.addNegativeKeywordList(sharedList);
} else if (criteriaType == CriteriaType.PLACEMENTS) {
campaignToAdd.addExcludedPlacementList(sharedList);
}
}
for (const campaignToRemove of campaignsToRemove) {
if (criteriaType == CriteriaType.KEYWORDS) {
campaignToRemove.removeNegativeKeywordList(sharedList);
} else if (criteriaType == CriteriaType.PLACEMENTS) {
campaignToRemove.removeExcludedPlacementList(sharedList);
}
}
return totalCampaigns;
}
/**
* Gets a list of campaigns having a particular label.
*
* @param {String} labelText The label text.
*
* @return {Array.<Number>} An array of campaign IDs having the specified
* label.
*/
function getLabelledCampaigns(labelText) {
const campaignIds = {};
let campaigns;
if (labelText != '') {
const label = getLabel(labelText);
campaigns = label.campaigns().withCondition(
'Status in [ENABLED, PAUSED]').get();
} else {
campaigns = AdsApp.campaigns().withCondition(
'Status in [ENABLED, PAUSED]').get();
}
for (const campaign of campaigns) {
campaignIds[campaign.getId()] = 1;
}
return campaignIds;
}
/**
* Gets a label with the specified label text.
*
* @param {String} labelText The label text.
*
* @return {AdsApp.Label} The label text.
*/
function getLabel(labelText) {
let labels = AdsApp.labels().withCondition(
"Name='" + labelText + "'").get();
if (labels.totalNumEntities() == 0) {
const message = Utilities.formatString(`Label named ${labelText} is missing in ' +
'your account. Make sure the label exists in the account, and is ' +
'applied to campaigns and adgroups you wish to process.`);
throw (message);
}
return labels.next();
}
/**
* Synchronizes the criteria in a shared negative criteria list with the user
* spreadsheet.
*
* @param {Object} config The configuration object.
* @param {String} criteriaType The criteria type for the shared negative list.
*
* @return {Object} A summary of the synced negative list, and the number of
* criteria that were synced.
*/
function syncCriteriaInNegativeList(config, criteriaType) {
let criteriaFromSheet = loadCriteria(criteriaType);
let totalCriteriaCount = Object.keys(criteriaFromSheet).length;
let sharedList = null;
let listName = config.listname[criteriaType];
sharedList = createNegativeListIfRequired(listName, criteriaType);
let negativeCriteria = null;
try {
if (criteriaType == CriteriaType.KEYWORDS) {
negativeCriteria = sharedList.negativeKeywords().get();
} else if (criteriaType == CriteriaType.PLACEMENTS) {
negativeCriteria = sharedList.excludedPlacements().get();
}
} catch (e) {
console.error(`Failed to retrieve shared list. Error says ${e}`);
if (AdsApp.getExecutionInfo().isPreview()) {
const message = Utilities.formatString(`The script cannot create` +
` the negative ${criteriaType} list in preview mode. Either run` +
` the script without preview, or create a negative ` +
` ${criteriaType} list with name "${listName}" ` +
` manually before previewing the script.`);
console.log(message);
}
throw e;
}
const criteriaToDelete = [];
for (const negativeCriterion of negativeCriteria) {
let key = null;
if (criteriaType == CriteriaType.KEYWORDS) {
key = negativeCriterion.getText();
// Since the keyword text in the spreadsheet specifies match types in the
// syntax accepted by the UI, we need to convert our keys to match it.
const matchType = negativeCriterion.getMatchType();
if (matchType === "PHRASE") {
key = `"${key}"`;
} else if (matchType === "EXACT") {
key = `[${key}]`;
}
} else if (criteriaType == CriteriaType.PLACEMENTS) {
key = negativeCriterion.getUrl();
}
if (key in criteriaFromSheet) {
// Nothing to do with this criteria. Remove it from loaded list.
delete criteriaFromSheet[key];
} else {
// This criterion is not in the sync list. Mark for deletion.
criteriaToDelete.push(negativeCriterion);
}
}
// Whatever left in the sync list are new items.
if (criteriaType == CriteriaType.KEYWORDS) {
sharedList.addNegativeKeywords(Object.keys(criteriaFromSheet));
} else if (criteriaType == CriteriaType.PLACEMENTS) {
sharedList.addExcludedPlacements(Object.keys(criteriaFromSheet));
}
for (let i = 0; i < criteriaToDelete.length; i++) {
criteriaToDelete[i].remove();
}
return {
'SharedList': sharedList,
'CriteriaCount': totalCriteriaCount,
'Type': criteriaType
};
}
/**
* Creates a shared negative criteria list if required.
*
* @param {string} listName The name of shared negative criteria list.
* @param {String} listType The criteria type for the shared negative list.
*
* @return {AdsApp.NegativeKeywordList|AdsApp.ExcludedPlacementList} An
* existing shared negative criterion list if it already exists in the
* account, or the newly created list if one didn't exist earlier.
*/
function createNegativeListIfRequired(listName, listType) {
let negativeListSelector = null;
if (listType == CriteriaType.KEYWORDS) {
negativeListSelector = AdsApp.negativeKeywordLists();
} else if (listType == CriteriaType.PLACEMENTS) {
negativeListSelector = AdsApp.excludedPlacementLists();
}
let negativeListIterator = negativeListSelector.withCondition(
`Name = '${listName}'`).get();
if (negativeListIterator.totalNumEntities() == 0) {
let builder = null;
if (listType == CriteriaType.KEYWORDS) {
builder = AdsApp.newNegativeKeywordListBuilder();
} else if (listType == CriteriaType.PLACEMENTS) {
builder = AdsApp.newExcludedPlacementListBuilder();
}
let negativeListOperation = builder.withName(listName).build();
return negativeListOperation.getResult();
} else {
return negativeListIterator.next();
}
}
/**
* Loads a list of criteria from the user spreadsheet.
*
* @param {string} sheetName The name of shared negative criteria list.
*
* @return {Object} A map of the list of criteria loaded from the spreadsheet.
*/
function loadCriteria(sheetName) {
const spreadsheet = validateAndGetSpreadsheet(SPREADSHEET_URL);
const sheet = spreadsheet.getSheetByName(sheetName);
const values = sheet.getRange('B4:B').getValues();
const retval = {};
for (const value of values) {
let keyword = value[0].toString().trim();
if (keyword != '') {
retval[keyword] = 1;
}
}
return retval;
}
/**
* Loads a configuration object from the spreadsheet.
*
* @return {Object} A configuration object.
*/
function readConfig() {
const spreadsheet = validateAndGetSpreadsheet(SPREADSHEET_URL);
let values = spreadsheet.getRangeByName('ConfigurationValues').getValues();
const config = {
'label': values[0][0],
'listname': {
},
'email': values[3][0],
};
config.listname[CriteriaType.KEYWORDS] = values[1][0];
config.listname[CriteriaType.PLACEMENTS] = values[2][0];
return config;
}
/**
* DO NOT EDIT ANYTHING BELOW THIS LINE.
* Please modify your spreadsheet URL 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 == '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.');
}
return SpreadsheetApp.openByUrl(spreadsheeturl);
}