Christof Van Dun <
chri...@amiforyou.com>
Tue 15 Jul, 13:46 (7 days ago)
to googleadsscripts-support
Hello,
Can you please assist us in adjusting the following script, this so we can use it to check whether a dealer (in a campaign name) is also mentioned in the final url and in the adcopy.
You can find this script in Google Ads account 751-069-3948.
Thank you for your help.
---
/**
* This Google Ads Script checks if a dealer name, extracted from the campaign name,
* matches the final URL and ad copy of all active ads within active ad groups.
*
* The script assumes the dealer name is located in the campaign name after the first underscore.
* For example, if a campaign is named "Brand_DealerName_CampaignType", it will extract "DealerName".
*
* It generates a report in a new Google Sheet, detailing matches and discrepancies.
*/
function main() {
// --- Configuration ---
// Name for the Google Sheet where the report will be generated.
const SPREADSHEET_NAME = 'Google Ads Dealer Name Verification Report';
// Sheet headers for the report.
const HEADERS = [
'Campaign Name',
'Campaign ID',
'Dealer Name (from Campaign Name)',
'Ad Group Name',
'Ad Group ID',
'Ad ID',
'Ad Type',
'Final URL',
'Ad Copy (Headlines & Descriptions)',
'URL Contains Dealer Name?',
'Ad Copy Contains Dealer Name?',
'Discrepancy Details'
];
// Get or create the spreadsheet for reporting
const spreadsheet = getOrCreateSpreadsheet(SPREADSHEET_NAME);
const sheet = spreadsheet.getActiveSheet();
// Clear previous content and set headers
sheet.clearContents();
sheet.appendRow(HEADERS);
sheet.setFrozenRows(1); // Freeze the header row
sheet.getRange(1, 1, 1, HEADERS.length).setBackground('#cfe2f3').setFontWeight('bold'); // Style headers
Logger.log(`Starting Google Ads Dealer Name Verification Script.`);
Logger.log(`Report will be generated in: ${spreadsheet.getUrl()}`);
let campaignsIterator;
try {
// Get all active campaigns
campaignsIterator = AdsApp.campaigns()
.withCondition('Status = ENABLED')
.get();
} catch (e) {
Logger.log(`Error fetching campaigns: ${e.message}`);
sheet.appendRow(['Error fetching campaigns:', e.message]);
return;
}
if (!campaignsIterator.hasNext()) {
Logger.log('No active campaigns found.');
sheet.appendRow(['No active campaigns found.']);
return;
}
let totalDiscrepancies = 0;
while (campaignsIterator.hasNext()) {
const campaign = campaignsIterator.next();
const campaignName = campaign.getName();
const campaignId = campaign.getId();
// Extract dealer name from campaign name (text after the first underscore)
const dealerNameMatch = campaignName.match(/_(.+?)_/); // Gets text between first and second underscore
let dealerName = '';
if (dealerNameMatch && dealerNameMatch[1]) {
dealerName = dealerNameMatch[1].trim().toLowerCase();
} else {
Logger.log(`Skipping campaign '${campaignName}' (ID: ${campaignId}) - Could not extract dealer name from campaign name.`);
sheet.appendRow([
campaignName,
campaignId,
'N/A (No underscore found for dealer name)',
'', '', '', '', '', '',
'N/A', 'N/A', 'Could not extract dealer name from campaign name. Expected format: Brand_DealerName_CampaignType'
]);
continue; // Skip to the next campaign
}
Logger.log(`Processing campaign: '${campaignName}' (ID: ${campaignId}) - Extracted Dealer Name: '${dealerName}'`);
let adGroupsIterator;
try {
// Get all active ad groups within the current campaign
adGroupsIterator = campaign.adGroups()
.withCondition('Status = ENABLED')
.get();
} catch (e) {
Logger.log(`Error fetching ad groups for campaign '${campaignName}': ${e.message}`);
sheet.appendRow([
campaignName, campaignId, dealerName,
'Error fetching ad groups', '', '', '', '', '',
'N/A', 'N/A', `Error: ${e.message}`
]);
continue;
}
if (!adGroupsIterator.hasNext()) {
Logger.log(`No active ad groups found for campaign '${campaignName}'.`);
sheet.appendRow([
campaignName, campaignId, dealerName,
'No active ad groups', '', '', '', '', '',
'N/A', 'N/A', 'No active ad groups found'
]);
continue;
}
while (adGroupsIterator.hasNext()) {
const adGroup = adGroupsIterator.next();
const adGroupName = adGroup.getName();
const adGroupId = adGroup.getId();
let adsIterator;
try {
// Get all active ads within the current ad group
adsIterator = adGroup.ads()
.withCondition('Status = ENABLED')
.get();
} catch (e) {
Logger.log(`Error fetching ads for ad group '${adGroupName}' in campaign '${campaignName}': ${e.message}`);
sheet.appendRow([
campaignName, campaignId, dealerName,
adGroupName, adGroupId,
'Error fetching ads', '', '', '',
'N/A', 'N/A', `Error: ${e.message}`
]);
continue;
}
if (!adsIterator.hasNext()) {
Logger.log(`No active ads found for ad group '${adGroupName}' in campaign '${campaignName}'.`);
sheet.appendRow([
campaignName, campaignId, dealerName,
adGroupName, adGroupId,
'No active ads', '', '', '',
'N/A', 'N/A', 'No active ads found'
]);
continue;
}
while (adsIterator.hasNext()) {
const ad = adsIterator.next();
const adId = ad.getId();
const adType = ad.getType();
let finalUrl = '';
let adCopy = '';
let urlMatches = false;
let adCopyMatches = false;
let discrepancyDetails = '';
try {
finalUrl = ad.urls().getFinalUrl() || '';
// Check if dealer name is in the URL (case-insensitive)
urlMatches = finalUrl.toLowerCase().includes(dealerName);
// Get ad copy based on ad type
if (adType === 'EXPANDED_TEXT_AD') {
const eta = ad.asExpandedTextAd();
adCopy += (eta.getHeadlinePart1() || '') + ' ';
adCopy += (eta.getHeadlinePart2() || '') + ' ';
adCopy += (eta.getHeadlinePart3() || '') + ' ';
adCopy += (eta.getDescription1() || '') + ' ';
adCopy += (eta.getDescription2() || '');
} else if (adType === 'RESPONSIVE_SEARCH_AD') {
const rsa = ad.asResponsiveSearchAd();
rsa.getHeadlines().forEach(h => adCopy += h.getText() + ' ');
rsa.getDescriptions().forEach(d => adCopy += d.getText() + ' ');
} else if (adType === 'CALL_ONLY_AD') {
const coa = ad.asCallOnlyAd();
adCopy += (coa.getDescription1() || '') + ' ';
adCopy += (coa.getDescription2() || '');
}
// Add other ad types as needed (e.g., HTML5_AD, IMAGE_AD, DYNAMIC_SEARCH_AD, etc.)
// For ad types like IMAGE_AD or HTML5_AD, checking ad copy might not be relevant.
// You might want to skip ad copy check for these or add specific logic.
else {
Logger.log(`Unsupported ad type '${adType}' for ad ID ${adId}. Skipping ad copy check.`);
adCopy = `(Ad Type: ${adType} - Ad copy check skipped)`;
adCopyMatches = true; // Assume match if not checking
}
// Check if dealer name is in the ad copy (case-insensitive)
adCopyMatches = adCopy.toLowerCase().includes(dealerName);
// Determine discrepancies
if (!urlMatches || !adCopyMatches) {
totalDiscrepancies++;
if (!urlMatches) {
discrepancyDetails += 'Dealer name NOT found in Final URL. ';
}
if (!adCopyMatches) {
discrepancyDetails += 'Dealer name NOT found in Ad Copy. ';
}
Logger.log(`Discrepancy found for Ad ID ${adId} in Ad Group '${adGroupName}' (Campaign: '${campaignName}'). Details: ${discrepancyDetails}`);
}
} catch (e) {
Logger.log(`Error processing ad ID ${adId} in ad group '${adGroupName}': ${e.message}`);
discrepancyDetails = `Error processing ad: ${e.message}`;
urlMatches = 'ERROR';
adCopyMatches = 'ERROR';
}
// Append row to the spreadsheet
sheet.appendRow([
campaignName,
campaignId,
dealerName,
adGroupName,
adGroupId,
adId,
adType,
finalUrl,
adCopy.trim(),
urlMatches ? 'Yes' : 'No',
adCopyMatches ? 'Yes' : 'No',
discrepancyDetails.trim()
]);
}
}
}
Logger.log(`Script finished. Total discrepancies found: ${totalDiscrepancies}`);
Logger.log(`Report available at: ${spreadsheet.getUrl()}`);
}
/**
* Gets an existing Google Sheet by name or creates a new one if it doesn't exist.
* @param {string} spreadsheetName The name of the spreadsheet.
* @returns {GoogleAppsScript.Spreadsheet.Spreadsheet} The spreadsheet object.
*/
function getOrCreateSpreadsheet(spreadsheetName) {
const files = DriveApp.getFilesByName(spreadsheetName);
if (files.hasNext()) {
const file = files.next();
return SpreadsheetApp.open(file);
} else {
// Create a new spreadsheet in the root folder
const newSpreadsheet = SpreadsheetApp.create(spreadsheetName);
Logger.log(`Created new spreadsheet: ${newSpreadsheet.getUrl()}`);
return newSpreadsheet;
}
}
--
Met vriendelijke groeten | Cordialement | Best regards
Christof van Dun
Online Marketeer
amiforyou
T |
+32 3 349 18 10De Gerlachekaai 20, 2000 Antwerpen
amiforyou.comamiforyou LinkedIn Facebook Instagram