使用 AI 实现 Google Ads 否定关键词自动化

使用 AI 实现 Google Ads 否定关键词自动化

FlowHunt 通过 AI 自动化管理 Google Ads 否定关键词,帮助企业降本增效,优化广告投入,轻松提升活动效果。

理解无效关键词花费

当您的广告出现在与您所售产品不匹配的搜索结果中时,就会发生无效关键词花费。这会迅速消耗您的预算,因为每一次无关的点击都需要花钱,但却无法带来销售。例如,如果一家销售高端皮鞋的公司使用了“买鞋”这样的宽泛关键词,可能会吸引对运动鞋或凉鞋感兴趣的人点击,而这些并不是他们的目标客户。这种关键词定向不匹配会导致广告花费浪费,投资回报率下降。企业需要理解这个概念,才能避免不必要的经济损失,把预算集中在正确的关键词上。

否定关键词的作用

否定关键词是任何 Google Ads 活动中必不可少的工具。它们允许广告主排除特定搜索词,从而保证只有相关的搜索才会触发广告展示。例如,使用“便宜”或“打折”这样的否定关键词,可以让皮鞋公司避免吸引对高端产品无兴趣的人点击。通过精心制定否定关键词列表,企业能够优化广告支出,减少无效点击,并提升整体活动效果。

用 AI 管理关键词

人工智能(AI)正在改变广告主管理 Google Ads 活动的方式。像 FlowHunt 这样的 AI 工具专为关键词分组设计,帮助企业更有效地识别和组织相关关键词。这种自动化简化了寻找正向和否定关键词的流程,大大减少了人工管理活动的工作量。AI 驱动的关键词管理能够根据表现数据实时调整,确保广告支出持续优化,实现最佳投资回报。

降低无效关键词花费的策略

为了减少无效关键词花费,企业应采取多种策略:

  • 定期更新关键词列表,以适应市场趋势和消费者兴趣的变化。
  • 实施强有力的否定关键词策略,持续调研和更新需要排除的词汇。
  • 根据表现数据监控和调整活动
  • 分析哪些关键词带来销售,哪些没有,以优化定向并最大化广告支出。

案例分析:PostAffiliatePro 的方法

PostAffiliatePro 曾面临每月广告支出困扰,难以从 Google Ads 活动中获得理想的投资回报率。他们决定利用 AI 来解决这个问题。通过引入 AI 工具,他们自动化了关键词管理流程,更精准地识别正向和否定关键词。这一改变帮助他们优化了广告预算,显著降低了成本,提高了活动效率。他们的经验凸显了用 AI 技术高效管理关键词花费的益处。每个新关键词的分析会在首次展示后一小时内完成。这种速度有助于在访客点击 Google 广告前及时捕捉否定关键词。

improved conversion rate 转化率提升(只有相关搜索带来更多销售)

拓展资源

如果您有兴趣进一步了解高效关键词管理和 AI 优化,可参考以下资源:

用于评估每个新关键词的 Google Ads 脚本

以下是我们的脚本,每小时运行一次,用于评估关键词集群。

该脚本旨在自动化 Google Ads 活动管理的多个任务。它与 Google 表格进行配置和设置交互,并在 Google Ads 账户中执行操作,如分析搜索词、添加或排除关键词,以及通过 FlowHunt API 实现高级 AI 关键词聚类。

Google Sheet to manage script settings 用于管理脚本设置的 Google 表格

Negative keywords automatically applied to Ad groups 否定关键词自动应用到广告组

主要功能

  • 核心控制逻辑位于 main() 函数中。该函数会打开通过 spreadsheetURL 指定的 Google 表格,获取如 apiKeycountrylanguage 等必要配置。
  • 系统首先尝试将新的正向关键词添加到 FlowHunt 集群。如果成功,则继续分析尚未分配的搜索词。

关键词分析

  • 分析未分配词汇: 该函数遍历账户中所有带有特定标签的广告组。它检索 Google Ads 中尚未被定向且至少有一次展示的搜索词。
  • 系统利用 FlowHunt API 查找与当前分析关键词相似的关键词,并根据设定的 minimumMatch 标准进行筛选。
  • 符合标准的搜索词会被添加为正向关键词,如果未达到标准,则作为否定关键词标记,并在 Google 表格和 Google Ads 活动中相应更新。

FlowHunt 集成

  • API 交互: 脚本依赖 callFlowHuntApi() 函数与 FlowHunt API 进行各种任务,包括获取工作区 ID 和关键词聚类。
  • 添加关键词到集群: 该功能将正向关键词推送回 FlowHunt 进行聚类,确保其由 Google Ads 实时搜索查询数据驱动。
  • 新增和否定关键词会记录在不同的表单中,便于持续跟踪和审核。

使用方法

部署该脚本,用户需:

  • 提供有效的 Google 表格 URL,并确保包含必要的表单(“Settings”、“AddedKW”、“NegativeKW”)。
  • 在 Google 表格中正确配置 FlowHunt API 密钥、国家代码及其他操作设置。
  • 确保脚本在已启用适当 API 访问权限的 Google Ads Script 环境下执行。

Add script to your Google Ads in menu Tools -> Bulk Actions -> Scripts 在 Google Ads 菜单“工具 -> 批量操作 -> 脚本”中添加脚本。

请务必设置您自己的 Google 表格真实链接。剩下的交给我们的魔法吧。我们会识别属于活动的关键词,并实现关键词(包括否定与正向)的自动化管理。

//Global variables
var spreadsheetURL;
var spreadsheet;
var sheetSettings; 
var sheetAddedKW;
var sheetNegativeKW;
var apiKey;
var labelName;
var country;
var language;
var location;
var urlsCount;
var minimumMatch;
var workspaceId;

function main() {
    // Provide the Google Sheets URL here
  spreadsheetURL = "https://docs.google.com/spreadsheets/d/....... FULL URL TO GOOGLE SHEET";
  spreadsheet = SpreadsheetApp.openByUrl(spreadsheetURL);
  sheetSettings = spreadsheet.getSheetByName("Settings"); 
  sheetAddedKW = spreadsheet.getSheetByName("AddedKW");
  sheetNegativeKW = spreadsheet.getSheetByName("NegativeKW");
  apiKey = getSettingValue("FlowHuntAPIkey")
  labelName = getSettingValue("LabelName")
  country = getSettingValue("CountryCode")
  language = getSettingValue("LanguageCode")
  location = getSettingValue("Location")

  urlsCount = getSettingValue("TopUrlsCount")
  minimumMatch = getSettingValue("MinimumMatch")
  workspaceId = getWorkspaceId()
  
  if (workspaceId.length < 10) {
    Logger.log("Failed to load workspace id from FlowHunt, check API key");
    return;
  }
  
  Logger.log("FlowHunt WorkspaceId: " + workspaceId);
  
  if (addPositiveKWsToCluster() == 0) {
    // Analyze new keywords just if all positive keywords added already
    analyzeNotAssignedWords();
  }
}

function analyzeNotAssignedWords() {
  Logger.log("*** START Checking not assigned keywords");
  
  // Iterate through all ad groups in the account
  var adGroupsIterator = AdsApp.adGroups().get();
  
  while (adGroupsIterator.hasNext()) {
    var adGroup = adGroupsIterator.next();
    var groupName = adGroup.getId() + " - " + adGroup.getName();
    if (hasLabel(adGroup, labelName)) {
      // Get the search terms for the current ad group ordered by clicks in the last X days
      var searchTermsQuery = "SELECT Query FROM SEARCH_QUERY_PERFORMANCE_REPORT " +
        "WHERE AdGroupId = " + adGroup.getId() +
        " AND QueryTargetingStatus = \"NONE\" " +
        "DURING TODAY";

      var searchTermsIterator = AdsApp.report(searchTermsQuery).rows();
      var adGroupKeywords = [];
      while (searchTermsIterator.hasNext()) {
        var searchTerm = searchTermsIterator.next();
        var searchTermText = searchTerm["Query"].trim();
        var similarQueries = getSimilarQueries(groupName, searchTermText)
        var filteredSimilarQueries = getFilteredSimilarQueries(similarQueries);
        if (filteredSimilarQueries.length > 0) {
          var keywordOperation = adGroup.newKeywordBuilder().withText("[" + searchTermText + "]").build();
          if (keywordOperation.isSuccessful()) {
            adGroupKeywords.push(searchTermText);
            var rowData = [groupName, searchTermText, new Date(), "ADDING AS POSITIVE, REVIEW!", JSON.stringify(filteredSimilarQueries)];
            sheetAddedKW.appendRow(rowData);
          } else {
            Logger.log("Failed to add keyword as positive:" + searchTermText)
          }
        } else {
          // add to negative
           adGroup.createNegativeKeyword("[" + searchTermText + "]");
          Logger.log("Excluded search term in ad group '" + groupName + "': " + searchTermText);
          var rowData = [groupName, "[" + searchTermText + "]", new Date(), JSON.stringify(similarQueries)];
          sheetNegativeKW.appendRow(rowData);
        }
      }
      if (adGroupKeywords.length > 0) {
        //Add all keywords in the list to FlowHunt Cluster
        addKeywordsToFlowHunt(groupName, adGroupKeywords);
      }
    }
  }
  Logger.log("*** FINISHED Checking not assigned keywords");
}

function getSimilarQueries(groupName, query) {
  result = callFlowHuntApi("/serp/serp/cluster/query_intersections?workspace_id="+workspaceId, "POST", {
    "query": query,
    "country": country,
    "language": language,
    "location": location,
    "group_name": groupName,
    "live_mode": true,
    "max_position": urlsCount
  });
  Logger.log(result)
  if (result.status=="SUCCESS") {
    return JSON.parse(result.result);
  }
  return []
}

function getFilteredSimilarQueries(similarQueries) {
  filtered = [];
  for (var i=1; i<similarQueries.length; i++){
    if (similarQueries[i].count>=minimumMatch) {
      filtered.push(similarQueries[i]);
    }
  }
  return filtered;
}

function addPositiveKWsToCluster() {
  Logger.log("*** START Checking new campaign keywords");
  // Iterate through all ad groups in the account
  var adGroupsIterator = AdsApp.adGroups().get();
  var processedKWs = sheetAddedKW.getDataRange().getValues();
  var processedKWsMap = {};
  var rowsAdded = 0;
  
  for (var i = 1; i < processedKWs.length; i++) { // Start at 1 to skip header row if exists
    var groupName = processedKWs[i][0];
    var keyword = processedKWs[i][1];
    processedKWsMap[groupName + '|' + keyword] = true;
  }
  
  while (adGroupsIterator.hasNext()) {
    var adGroup = adGroupsIterator.next();
    var groupName = adGroup.getId() + " - " + adGroup.getName();
    if (hasLabel(adGroup, labelName)) {
      var keywordsIterator = adGroup.keywords().get();
      var adGroupKeywords = [];
      while (keywordsIterator.hasNext()) {
        var keyword = keywordsIterator.next();
        if (keyword.isEnabled()) {
          var key = groupName + '|' + keyword.getText();
          if (!processedKWsMap[key]) {
            adGroupKeywords.push(keyword.getText());
            var rowData = [groupName, keyword.getText(), new Date(), "Already present in campaign"];
            sheetAddedKW.appendRow(rowData);
            processedKWsMap[key] = true; 
          }
        }
      }
      if (adGroupKeywords.length > 0) {
        //Add all keywords in the list to FlowHunt Cluster
        addKeywordsToFlowHunt(groupName, adGroupKeywords);
      } else {
        Logger.log("No new keywords in Group: " + groupName);
      }
      rowsAdded = rowsAdded + adGroupKeywords.length
    }
  }
  Logger.log("*** FINISHED Checking new campaign keywords");
  return rowsAdded;
}

function addKeywordsToFlowHunt(GroupName, adGroupKeywords) {
  requests = []
  adGroupKeywords.forEach(function(keyword) {
    requests.push(
        {
          "query": keyword,
          "country": country,
          "language": language,
          "location": location,
          "group_name": GroupName,
          "count_urls": 30
        }
      );
    });
  callFlowHuntApi("/serp/serp/cluster/add_queries?workspace_id="+workspaceId, "POST", {"requests":requests});
}

function getSettingValue(settingName) {
    var data = sheetSettings.getDataRange().getValues();
    for (var i = 0; i < data.length; i++) {
        if (data[i][0] === settingName) {
            return data[i][1];
        }
    }
    return null;
}
  
function getWorkspaceId() {
  result = callFlowHuntApi("/auth/me", "GET")
  if (result !== null) {
    return result.api_key_workspace_id;
  }
}

function callFlowHuntApi(endpoint, method, requestBody) {
    var url = "https://api.flowhunt.io/v2" + endpoint;
    var headers = {
        "Api-Key": apiKey,
        "Content-Type": "application/json"
    };
    var options = {
        "method" : method,  // or "post", "put", etc.
        "headers" : headers,
        "payload": JSON.stringify(requestBody)
    };
    
    try {
        var response = UrlFetchApp.fetch(url, options);
        var responseData = JSON.parse(response.getContentText());
        Logger.log(responseData);
        return responseData;
    } catch (e) {
        Logger.log("An error occurred: " + e.message);
    }
    return null;
}

function hasLabel(adGroup, labelName) {
  var labels = adGroup.labels().get();
  while (labels.hasNext()) {
    var label = labels.next();
    if (label.getName() === labelName) {
      Logger.log("Processing Adgroup " + adGroup.getName());
      return true;
    }
  }
  return false;
}

常见问题

为什么在 Google Ads 中否定关键词很重要?

否定关键词可以防止您的广告出现在不相关的搜索中,减少浪费支出,并通过专注于最相关的查询提升活动投资回报率。

AI 如何自动化否定关键词管理?

像 FlowHunt 这样的 AI 工具会分析实时搜索数据,识别无关关键词,并自动用否定关键词更新您的活动,节省时间并提升效率。

用 AI 自动化否定关键词后可以获得哪些效果?

可以期待转化率提升,减少因无关点击导致的广告支出,并通过 AI 持续优化关键词列表和定向使活动更加高效。

如何为我的 Google Ads 设置 FlowHunt 自动化?

您需要连接 Google Ads 账户,在 Google 表格中配置设置,并部署提供的脚本,以启用由 FlowHunt AI 驱动的自动关键词管理。

Viktor Zeman 是 QualityUnit 的共同所有人。即使在领导公司 20 年后,他仍然主要是一名软件工程师,专注于人工智能、程序化 SEO 和后端开发。他参与了众多项目,包括 LiveAgent、PostAffiliatePro、FlowHunt、UrlsLab 等等。

Viktor Zeman
Viktor Zeman
首席执行官,人工智能工程师

用 AI 自动化您的 Google Ads

准备好提升您的 Google Ads 投资回报率了吗?了解 AI 如何自动化管理否定关键词并优化广告花费。

了解更多

PPC AI智能代理,自动化否定关键词管理
PPC AI智能代理,自动化否定关键词管理

PPC AI智能代理,自动化否定关键词管理

借助FlowHunt的PPC AI智能代理,实现Google广告否定关键词自动化。排除无关查询,减少浪费支出,并通过AI驱动的精准投放和轻松优化提升转化率。...

1 分钟阅读
PPC AI +3
通过PPC管理自动化革命性提升您的广告活动:提升转化率与效率
通过PPC管理自动化革命性提升您的广告活动:提升转化率与效率

通过PPC管理自动化革命性提升您的广告活动:提升转化率与效率

了解PPC管理自动化及先进关键词优化策略如何帮助您将转化率提升一倍、降低广告花费并简化广告活动流程。探索FlowHunt软件如何利用关键词聚类、否定关键词及数据驱动洞察,实现B2B与电商的成功。...

1 分钟阅读
PPC Keyword Optimization +6
理解PPC优化与关键词分组
理解PPC优化与关键词分组

理解PPC优化与关键词分组

学习如何优化PPC广告活动并掌握关键词分组,从而提高相关性、提升质量得分、获得更高ROI,并有效管理广告活动。探索结构化广告组、优化着陆页和使用否定关键词的实用技巧、工具与最佳实践。...

2 分钟阅读
PPC Keyword Grouping +4