/** * * Search Query Mining Tool * * This script calculates the contribution of each word or phrase found in the * search query report and outputs a report into a Google Doc spreadsheet. * * Version: 2.2 * Updated 2015-09-17: replacing 'KeywordText' with 'Criteria' * Updated 2016-10-11: replacing 'ConvertedClicks' with 'Conversions' * Google AdWords Script maintained on brainlabsdigital.com * **/ function main() { ////////////////////////////////////////////////////////////////////////////// // Options var startDate = "2017-01-01"; var endDate = "2015-09-30"; // The start and end date of the date range for your search query data // Format is yyyy-mm-dd var currencySymbol = "€"; // The currency symbol used for formatting. For example "£", "$" or "€". var campaignNameContains = ""; // Use this if you only want to look at some campaigns // such as campaigns with names containing 'Brand' or 'Shopping'. // Leave as "" if not wanted. var campaignNameDoesNotContain = ""; // Use this if you want to exclude some campaigns // such as campaigns with names containing 'Brand' or 'Shopping'. // Leave as "" if not wanted. var ignorePausedCampaigns = true; // Set this to true to only look at currently active campaigns. // Set to false to include campaigns that had impressions but are currently paused. var ignorePausedAdGroups = true; // Set this to true to only look at currently active ad groups. // Set to false to include ad groups that had impressions but are currently paused. var checkNegatives = true; // Set this to true to remove queries that would be excluded by your negative keywords. var spreadsheetUrl = "https://docs.google.com/YOUR-SPREADSHEET-URL-HERE"; // The URL of the Google Doc the results will be put into. var minNGramLength = 1; var maxNGramLength = 4; // The word length of phrases to be checked. // For example if minNGramLength is 1 and maxNGramLength is 3, // phrases made of 1, 2 and 3 words will be checked. // Change both min and max to 1 to just look at single words. var clearSpreadsheet = true; ////////////////////////////////////////////////////////////////////////////// // Thresholds var queryCountThreshold = 0; var impressionThreshold = 10; var clickThreshold = 0; var costThreshold = 0; var conversionThreshold = 0; // Words will be ignored if their statistics are lower than any of these thresholds ////////////////////////////////////////////////////////////////////////////// // Check the spreadsheet has been entered, and that it works if (spreadsheetUrl.replace(/[AEIOU]/g,"X") == "https://docs.google.com/YXXR-SPRXXDSHXXT-XRL-HXRX") { Logger.log("Problem with the spreadsheet URL: make sure you've replaces the default with a valid spreadsheet URL."); return; } try { var spreadsheet = SpreadsheetApp.openByUrl(spreadsheetUrl); } catch (e) { Logger.log("Problem with the spreadsheet URL: '" + e + "'"); return; } // Get the IDs of the campaigns to look at var dateRange = startDate.replace(/-/g, "") + "," + endDate.replace(/-/g, ""); var activeCampaignIds = []; var whereStatements = ""; if (campaignNameDoesNotContain != "") { whereStatements += "AND CampaignName DOES_NOT_CONTAIN_IGNORE_CASE '" + campaignNameDoesNotContain + "' "; } if (ignorePausedCampaigns) { whereStatements += "AND CampaignStatus = ENABLED "; } else { whereStatements += "AND CampaignStatus IN ['ENABLED','PAUSED'] "; } var campaignReport = AdWordsApp.report( "SELECT CampaignName, CampaignId " + "FROM CAMPAIGN_PERFORMANCE_REPORT " + "WHERE CampaignName CONTAINS_IGNORE_CASE '" + campaignNameContains + "' " + "AND Impressions > 0 " + whereStatements + "DURING " + dateRange ); var campaignRows = campaignReport.rows(); while (campaignRows.hasNext()) { var campaignRow = campaignRows.next(); activeCampaignIds.push(campaignRow["CampaignId"]); }//end while if (activeCampaignIds.length == 0) { Logger.log("Could not find any campaigns with impressions and the specified options."); return; } var whereAdGroupStatus = ""; if (ignorePausedAdGroups) { var whereAdGroupStatus = "AND AdGroupStatus = ENABLED "; } else { whereAdGroupStatus += "AND AdGroupStatus IN ['ENABLED','PAUSED'] "; } ////////////////////////////////////////////////////////////////////////////// // Find the negative keywords var negativesByGroup = []; var negativesByCampaign = []; var sharedSetData = []; var sharedSetNames = []; var sharedSetCampaigns = []; if (checkNegatives) { // Gather ad group level negative keywords var keywordReport = AdWordsApp.report( "SELECT CampaignId, AdGroupId, Criteria, KeywordMatchType " + "FROM KEYWORDS_PERFORMANCE_REPORT " + "WHERE Status = ENABLED AND IsNegative = TRUE " + whereAdGroupStatus + "AND CampaignId IN [" + activeCampaignIds.join(",") + "] " + "DURING " + dateRange ); var keywordRows = keywordReport.rows(); while (keywordRows.hasNext()) { var keywordRow = keywordRows.next(); if (negativesByGroup[keywordRow["AdGroupId"]] == undefined) { negativesByGroup[keywordRow["AdGroupId"]] = [[keywordRow["Criteria"].toLowerCase(),keywordRow["KeywordMatchType"].toLowerCase()]]; } else { negativesByGroup[keywordRow["AdGroupId"]].push([keywordRow["Criteria"].toLowerCase(),keywordRow["KeywordMatchType"].toLowerCase()]); } } // Gather campaign level negative keywords var campaignNegReport = AdWordsApp.report( "SELECT CampaignId, Criteria, KeywordMatchType " + "FROM CAMPAIGN_NEGATIVE_KEYWORDS_PERFORMANCE_REPORT " + "WHERE IsNegative = TRUE " + "AND CampaignId IN [" + activeCampaignIds.join(",") + "]" ); var campaignNegativeRows = campaignNegReport.rows(); while (campaignNegativeRows.hasNext()) { var campaignNegativeRow = campaignNegativeRows.next(); if (negativesByCampaign[campaignNegativeRow["CampaignId"]] == undefined) { negativesByCampaign[campaignNegativeRow["CampaignId"]] = [[campaignNegativeRow["Criteria"].toLowerCase(),campaignNegativeRow["KeywordMatchType"].toLowerCase()]]; } else { negativesByCampaign[campaignNegativeRow["CampaignId"]].push([campaignNegativeRow["Criteria"].toLowerCase(),campaignNegativeRow["KeywordMatchType"].toLowerCase()]); } } // Find which campaigns use shared negative keyword sets var campaignSharedReport = AdWordsApp.report( "SELECT CampaignName, CampaignId, SharedSetName, SharedSetType, Status " + "FROM CAMPAIGN_SHARED_SET_REPORT " + "WHERE SharedSetType = NEGATIVE_KEYWORDS " + "AND CampaignId IN [" + activeCampaignIds.join(",") + "]"); var campaignSharedRows = campaignSharedReport.rows(); while (campaignSharedRows.hasNext()) { var campaignSharedRow = campaignSharedRows.next(); if (sharedSetCampaigns[campaignSharedRow["SharedSetName"]] == undefined) { sharedSetCampaigns[campaignSharedRow["SharedSetName"]] = [campaignSharedRow["CampaignId"]]; } else { sharedSetCampaigns[campaignSharedRow["SharedSetName"]].push(campaignSharedRow["CampaignId"]); } } // Map the shared sets' IDs (used in the criteria report below) // to their names (used in the campaign report above) var sharedSetReport = AdWordsApp.report( "SELECT Name, SharedSetId, MemberCount, ReferenceCount, Type " + "FROM SHARED_SET_REPORT " + "WHERE ReferenceCount > 0 AND Type = NEGATIVE_KEYWORDS "); var sharedSetRows = sharedSetReport.rows(); while (sharedSetRows.hasNext()) { var sharedSetRow = sharedSetRows.next(); sharedSetNames[sharedSetRow["SharedSetId"]] = sharedSetRow["Name"]; } // Collect the negative keyword text from the sets, // and record it as a campaign level negative in the campaigns that use the set var sharedSetReport = AdWordsApp.report( "SELECT SharedSetId, KeywordMatchType, Criteria " + "FROM SHARED_SET_CRITERIA_REPORT "); var sharedSetRows = sharedSetReport.rows(); while (sharedSetRows.hasNext()) { var sharedSetRow = sharedSetRows.next(); var setName = sharedSetNames[sharedSetRow["SharedSetId"]]; if (sharedSetCampaigns[setName] !== undefined) { for (var i=0; i -1 )){ searchIsExcluded = true; break; } } } // Checks if the query is excluded by a campaign level negative if (!searchIsExcluded && negativesByCampaign[queryRow["CampaignId"]] !== undefined) { for (var i=0; i -1 )){ searchIsExcluded = true; break; } } } if (searchIsExcluded) {continue;} } var currentWords = queryRow["Query"].split(" "); if (campaignNGrams[queryRow["CampaignName"]] == undefined) { campaignNGrams[queryRow["CampaignName"]] = []; adGroupNGrams[queryRow["CampaignName"]] = {}; for (var n=minNGramLength; n 6) { wordLength = "7+"; } if (numberOfWords[wordLength] == undefined) { numberOfWords[wordLength] = []; } for (var i=0; i 0) { numberOfWords[wordLength][statColumns[i]] += stats[i]; } else { numberOfWords[wordLength][statColumns[i]] = stats[i]; } } // Splits the query into n-grams and records the stats for each for (var n=minNGramLength; n currentWords.length) { break; } var doneNGrams = []; for (var w=0; w < currentWords.length - n + 1; w++) { var currentNGram = '="' + currentWords.slice(w,w+n).join(" ") + '"'; if (doneNGrams.indexOf(currentNGram) < 0) { if (campaignNGrams[queryRow["CampaignName"]][n][currentNGram] == undefined) { campaignNGrams[queryRow["CampaignName"]][n][currentNGram] = {}; campaignNGrams[queryRow["CampaignName"]][n][currentNGram]["Query Count"] = 0; } if (adGroupNGrams[queryRow["CampaignName"]][queryRow["AdGroupName"]][n][currentNGram] == undefined) { adGroupNGrams[queryRow["CampaignName"]][queryRow["AdGroupName"]][n][currentNGram] = {}; adGroupNGrams[queryRow["CampaignName"]][queryRow["AdGroupName"]][n][currentNGram]["Query Count"] = 0; } if (totalNGrams[n][currentNGram] == undefined) { totalNGrams[n][currentNGram] = {}; totalNGrams[n][currentNGram]["Query Count"] = 0; } campaignNGrams[queryRow["CampaignName"]][n][currentNGram]["Query Count"]++; adGroupNGrams[queryRow["CampaignName"]][queryRow["AdGroupName"]][n][currentNGram]["Query Count"]++; totalNGrams[n][currentNGram]["Query Count"]++; for (var i=0; i 0) { campaignNGrams[queryRow["CampaignName"]][n][currentNGram][statColumns[i]] += stats[i]; } else { campaignNGrams[queryRow["CampaignName"]][n][currentNGram][statColumns[i]] = stats[i]; } if (adGroupNGrams[queryRow["CampaignName"]][queryRow["AdGroupName"]][n][currentNGram][statColumns[i]] > 0) { adGroupNGrams[queryRow["CampaignName"]][queryRow["AdGroupName"]][n][currentNGram][statColumns[i]] += stats[i]; } else { adGroupNGrams[queryRow["CampaignName"]][queryRow["AdGroupName"]][n][currentNGram][statColumns[i]] = stats[i]; } if (totalNGrams[n][currentNGram][statColumns[i]] > 0) { totalNGrams[n][currentNGram][statColumns[i]] += stats[i]; } else { totalNGrams[n][currentNGram][statColumns[i]] = stats[i]; } } doneNGrams.push(currentNGram); } } } } Logger.log("Finished analysing queries."); ////////////////////////////////////////////////////////////////////////////// // Output the data into the spreadsheet var wordLengthOutput = []; var wordLengthFormat = []; var outputs = []; var formats = []; for (var n=minNGramLength; n 0) { printline.push(adGroupNGrams[campaign][adGroup][n][nGram][multiplier] / adGroupNGrams[campaign][adGroup][n][nGram][divisor]); } else { printline.push("-"); } } outputs[n]['adgroup'].push(printline); formats[n]['adgroup'].push(["0","0","0"].concat(formatting)); } } } } // Organise the campaign level stats into an array for output for (var n=minNGramLength; n 0) { printline.push(campaignNGrams[campaign][n][nGram][multiplier] / campaignNGrams[campaign][n][nGram][divisor]); } else { printline.push("-"); } } outputs[n]['campaign'].push(printline); formats[n]['campaign'].push(["0","0"].concat(formatting)); } } } // Organise the account level stats into an array for output for (var n=minNGramLength; n 0) { printline.push(totalNGrams[n][nGram][multiplier] / totalNGrams[n][nGram][divisor]); } else { printline.push("-"); } } outputs[n]['total'].push(printline); formats[n]['total'].push(["0"].concat(formatting)); } } // Organise the word count analysis into an array for output for (var i = 1; i<8; i++) { if (i < 7) { var wordLength = i; } else { var wordLength = "7+"; } var printline = [wordLength]; if (numberOfWords[wordLength] == undefined) { printline.push([0,0,0,0,"-","-","-","-"]); } else { for (var s=0; s 0) { printline.push(numberOfWords[wordLength][multiplier] / numberOfWords[wordLength][divisor]); } else { printline.push("-"); } } } wordLengthOutput.push(printline); wordLengthFormat.push(formatting); } var filterText = ""; if (ignorePausedAdGroups) { filterText = "Active ad groups"; } else { filterText = "All ad groups"; } if (ignorePausedCampaigns) { filterText += " in active campaigns"; } else { filterText += " in all campaigns"; } if (campaignNameContains != "") { filterText += " containing '" + campaignNameContains + "'"; if (campaignNameDoesNotContain != "") { filterText += " and not containing '" + campaignNameDoesNotContain + "'"; } } else if (campaignNameDoesNotContain != "") { filterText += " not containing '" + campaignNameDoesNotContain + "'"; } // Find or create the required sheets var spreadsheet = SpreadsheetApp.openByUrl(spreadsheetUrl); var campaignNGramName = []; var adGroupNGramName = []; var totalNGramName = []; var campaignNGramSheet = []; var adGroupNGramSheet = []; var totalNGramSheet = []; for (var n=minNGramLength; n