最佳做法

本頁說明使用 Google Ads 指令碼進行開發的多種最佳做法。

選擇器

使用選擇器篩選

請盡可能使用篩選器,只要求所需的實體。套用適當的篩選器有下列優點:

  • 程式碼更簡單,也更容易瞭解。
  • 指令碼執行速度更快。

請比較下列程式碼片段:

程式設計方法 程式碼片段
使用選取器篩選 (建議)
var keywords = AdsApp.keywords()
    .withCondition('Clicks > 10')
    .forDateRange('LAST_MONTH')
    .get();
while (keywords.hasNext()) {
  var keyword = keywords.next();
  // Do work here.
}
程式碼中的篩選器 (不建議使用)
var keywords = AdsApp.keywords().get();

while (keywords.hasNext()) {
  var keyword = keywords.next();
  var stats = keyword.getStatsFor(
      'LAST_MONTH');
  if (stats.getClicks() > 10) {
    // Do work here.
  }
}

我們不建議使用第二種方法,因為系統會嘗試擷取清單 帳戶中所有的關鍵字都只對名單套用篩選器。

避免掃遍廣告活動階層

當您想要擷取特定層級的實體時,請使用集合 方法,而不是整個廣告活動階層。於 此外,也變得更簡單、效能也更加出色:系統 不需要讀取所有廣告活動和廣告群組。

請比較下列程式碼片段,瞭解如何擷取帳戶中的所有廣告:

程式設計方法 程式碼片段
使用適當的收集方法 (建議做法)
var ads = AdsApp.ads();

瀏覽階層 (不建議)
var campaigns = AdsApp.campaigns().get();
while (campaigns.hasNext()) {
  var adGroups = campaigns.next().
      adGroups().get();
  while (adGroups.hasNext()) {
    var ads = adGroups.next().ads().get();
    // Do your work here.
  }
}

我們不建議使用第二種方法,因為它會嘗試擷取整個物件階層 (廣告活動、廣告群組),但我們只需要廣告。

使用特定的父項存取子方法

有時候,您需要取得擷取物件的父項實體。在這種情況下,您應使用提供的存取子方法,而非擷取整個階層。

請比較下列程式碼片段,這段程式碼可擷取上個月有超過 50 次點擊的文字廣告廣告群組:

程式設計方法 程式碼片段
使用適當的家長存取子方法 (建議)
var ads = AdsApp.ads()
    .withCondition('Clicks > 50')
    .forDateRange('LAST_MONTH')
    .get();

while (ads.hasNext()) {
  var ad = ads.next();
  var adGroup = ad.getAdGroup();
  var campaign = ad.getCampaign();
  // Store (campaign, adGroup) to an array.
}
遍歷階層 (不建議)
var campaigns = AdsApp.campaigns().get();
while (campaigns.hasNext()) {
  var adGroups = campaigns.next()
      .adGroups()
      .get();
  while (adGroups.hasNext()) {
    var ads = adGroups.ads()
       .withCondition('Clicks > 50')
       .forDateRange('LAST_MONTH')
       .get();
    if (ads.totalNumEntities() > 0) {
      // Store (campaign, adGroup) to an array.
    }
  }
}

我們不建議使用第二種方法,因為這樣會擷取整個廣告活動 和廣告群組階層,但您只需要一小部分 。第一種方法會限制自己只擷取相關的廣告集合,並使用適當方法存取其父項物件。

使用特定的父項篩選器

如要存取特定廣告活動或廣告群組中的實體,請使用 ,而不要擷取階層,再透過階層週遊。

比較以下擷取文字廣告清單的程式碼片段 特定廣告活動和廣告群組過去獲得的點擊超過 50 次 每月。

程式設計方法 程式碼片段
使用適當的父層級篩選器 (建議做法)
var ads = AdsApp.ads()
    .withCondition('CampaignName = "Campaign 1"')
    .withCondition('AdGroupName = "AdGroup 1"')
    .withCondition('Clicks > 50')
    .forDateRange('LAST_MONTH')
    .get();

while (ads.hasNext()) {
  var ad = ads.next();
  var adGroup = ad.getAdGroup();
  var campaign = ad.getCampaign();
  // Store (campaign, adGroup, ad) to
  // an array.
}
遍歷階層 (不建議)
var campaigns = AdsApp.campaigns()
    .withCondition('Name = "Campaign 1"')
    .get();

while (campaigns.hasNext()) {
  var adGroups = campaigns.next()
      .adGroups()
      .withCondition('Name = "AdGroup 1"')
      .get();
  while (adGroups.hasNext()) {
    var ads = adGroups.ads()
       .withCondition('Clicks > 50')
       .forDateRange('LAST_MONTH')
       .get();
    while (ads.hasNext()) {
      var ad = ads.next();
      // Store (campaign, adGroup, ad) to
      // an array.
    }
  }
}

我們不建議採用第二種方法,因為這會重複執行帳戶中的廣告活動和廣告群組階層,但您只需要選取的廣告組合,以及其上層廣告活動和廣告群組。第一種方式會限制 對父系實體套用特定篩選器,疊代廣告清單 。

盡可能使用 ID 進行篩選

篩選實體時,建議您依據實體的 ID 篩選,而非其他欄位。

請參考以下用於選取廣告活動的程式碼片段。

程式設計方法 程式碼片段
依 ID 篩選 (建議)
var campaign = AdsApp.campaigns()
    .withIds([12345])
    .get()
    .next();
依名稱篩選 (較不理想)
var campaign = AdsApp.campaigns()
    .withCondition('Name="foo"')
    .get()
    .next();

由於我們要依據非 ID 欄位進行篩選,因此第二種方法較不理想。

盡可能依家長 ID 篩選

選取實體時,請盡可能依據父項 ID 進行篩選。這將 限制擷取的實體清單,以便加快查詢速度 篩選結果。

請參考下列程式碼片段,瞭解如何根據 ID 擷取 AdGroup。假設您已知父項廣告活動 ID。

程式設計方法 程式碼片段
依廣告活動和廣告群組 ID 篩選 (建議)
var adGroup = AdsApp.adGroups()
    .withIds([12345])
    .withCondition('CampaignId="54678"')
    .get()
    .next();
只依廣告群組 ID 篩選 (較不理想)
var adGroup = AdsApp.adGroups()
    .withIds([12345])
    .get()
    .next();

雖然兩個程式碼片段都會產生相同的結果,但程式碼片段 1 使用父項 ID (CampaignId="54678")進行額外篩選,可限制伺服器在篩選結果時要迭代的實體清單,讓程式碼更有效率。

篩選條件過多時使用標籤

如果篩選條件太多,不妨建立 標籤,並利用該標籤篩選 實體。

請參考以下程式碼片段來擷取廣告活動清單 。

程式設計方法 程式碼片段
使用標籤 (建議)
var label = AdsApp.labels()
    .withCondition('Name = "My Label"')
    .get()
    .next();
var campaigns = label.campaigns.get();
while (campaigns.hasNext()) {
  var campaign = campaigns.next();
  // Do more work
}
建立複雜的選取器 (不建議)
var campaignNames = [‘foo’, ‘bar’, ‘baz’];

for (var i = 0; i < campaignNames.length; i++) {
  campaignNames[i] = '"' + campaignNames[i] + '"';
}

var campaigns = AdsApp.campaigns
    .withCondition('CampaignName in [' + campaignNames.join(',') + ']')
    .get();

while (campaigns.hasNext()) {
  var campaign = campaigns.next();
  // Do more work.
}

雖然這兩種程式碼片段的成效都差不多 因此會產生更複雜的程式碼,因為 選取器就會提高將標籤套用至新實體也更加容易 而不是編輯指令碼並加入新實體

限制 IN 子句中的條件數量

執行指令碼時,常見的用途是執行報表,以取得 實體。開發人員通常想用 可使用 IN 子句篩選實體 ID 的 AWQL 查詢。在實體數量有限的情況下,這種做法會運作良好。不過,隨著查詢長度增加,以下兩個原因會導致指令碼效能降低:

  • 剖析較長的查詢需要較長的時間才能剖析。
  • 您在 IN 子句中新增的每個 ID 都是需要評估的額外條件,因此會耗費更多時間。

在這些情況下,建議您為實體套用標籤,且 ,然後依 LabelId 篩選。

程式設計方法 程式碼片段
套用標籤並依 labelID 篩選 (建議做法)
// The label applied to the entity is "Report Entities"
var label = AdsApp.labels()
    .withCondition('LabelName contains "Report Entities"')
    .get()
    .next();

var report = AdsApp.report('SELECT AdGroupId, Id, Clicks, ' +
    'Impressions, Cost FROM KEYWORDS_PERFORMANCE_REPORT ' +
    'WHERE LabelId = "' + label.getId() + '"');
使用 IN 子句建立較長的查詢 (不建議)
var report = AdsApp.report('SELECT AdGroupId, Id, Clicks, ' +
    'Impressions, Cost FROM KEYWORDS_PERFORMANCE_REPORT WHERE ' +
    'AdGroupId IN (123, 456) and Id in (123,345, 456…)');

帳戶最新消息

批次變更

當您變更 Google Ads 實體時,Google Ads 指令碼不會執行 變更。而是嘗試將多項變更合併為批次,以便發出可執行多項變更的單一要求。這個方法可加快指令碼速度,並減少 Google Ads 伺服器的負載。不過,有些程式碼模式會迫使 Google Ads 指令碼頻繁清除其批次作業,導致指令碼執行速度變慢。

你可以使用以下指令碼更新關鍵字清單的出價。

程式設計方法 程式碼片段
追蹤更新後的元素 (建議做法)
var keywords = AdsApp.keywords()
    .withCondition('Clicks > 50')
    .withCondition('CampaignName = "Campaign 1"')
    .withCondition('AdGroupName = "AdGroup 1"')
    .forDateRange('LAST_MONTH')
    .get();

var list = [];
while (keywords.hasNext()) {
  var keyword = keywords.next();
  keyword.bidding().setCpc(1.5);
  list.push(keyword);
}

for (var i = 0; i < list.length; i++) {
  var keyword = list[i];
  Logger.log('%s, %s', keyword.getText(),
      keyword.bidding().getCpc());
}
在緊密迴圈中擷取更新的元素 (不建議使用)
var keywords = AdsApp.keywords()
    .withCondition('Clicks > 50')
    .withCondition('CampaignName = "Campaign 1"')
    .withCondition('AdGroupName = "AdGroup 1"')
    .forDateRange('LAST_MONTH')
    .get();

while (keywords.hasNext()) {
  var keyword = keywords.next();
  keyword.bidding().setCpc(1.5);
  Logger.log('%s, %s', keyword.getText(),
      keyword.bidding().getCpc());
}

我們不建議使用第二種方法,因為呼叫 keyword.bidding().getCpc() 會強制 Google Ads 指令碼清除setCpc() 且一次只會執行一項作業。第一種方法與第二種方法相似,但由於 getCpc() 呼叫是在與呼叫 setCpc() 不同的迴圈中完成,因此可支援批次處理,這也是第一種方法的額外優點。

盡可能使用建構工具

Google Ads 指令碼支援兩種建立新物件的方法:建立工具和建立 方法。建構工具比建立方式更靈活, 存取透過 API 呼叫建立的物件。

請參考以下程式碼片段:

程式設計方法 程式碼片段
使用建構工具 (建議)
var operation = adGroup.newKeywordBuilder()
    .withText('shoes')
    .build();
var keyword = operation.getResult();
使用建立方法 (不建議使用)
adGroup.createKeyword('shoes');
var keyword = adGroup.keywords()
    .withCondition('KeywordText="shoes"')
    .get()
    .next();

由於擷取關鍵字時會涉及額外的選取作業,因此不建議採用第二種方法。此外,建立方法也已淘汰。

不過請注意,建構工具使用不當時,Google 可能會 批次處理廣告指令碼。

請參考以下程式碼片段來建立關鍵字清單。 並列印新建關鍵字的 ID:

程式設計方法 程式碼片段
追蹤更新後的元素 (建議做法)
var keywords = [‘foo’, ‘bar’, ‘baz’];

var list = [];
for (var i = 0; i < keywords.length; i++) {
  var operation = adGroup.newKeywordBuilder()
      .withText(keywords[i])
      .build();
  list.push(operation);
}

for (var i = 0; i < list.length; i++) {
  var operation = list[i];
  var result = operation.getResult();
  Logger.log('%s %s', result.getId(),
      result.getText());
}
在緊密迴圈中擷取更新的元素 (不建議使用)
var keywords = [‘foo’, ‘bar’, ‘baz’];

for (var i = 0; i < keywords.length; i++) {
  var operation = adGroup.newKeywordBuilder()
      .withText(keywords[i])
      .build();
  var result = operation.getResult();
  Logger.log('%s %s', result.getId(),
      result.getText());
}

第二種方法會呼叫 operation.getResult(),因此不建議使用 和執行 Google Ads 指令碼的相同迴圈 一次執行一項作業雖然第一種方法與第二種方法相似,但由於我們在與建立位置不同的迴圈中呼叫 operation.getResult(),因此可以進行批次處理。

建議使用大量上傳功能處理大量更新

開發人員執行的常見工作是執行報表並更新實體 根據目前成效值計算出屬性 (例如關鍵字出價)。當您需要更新大量實體時,大量上傳功能通常可提供更佳效能。舉例來說,假設下列指令碼 上個月TopImpressionPercentage > 0.4 的關鍵字 MaxCpc:

程式設計方法 程式碼片段
使用大量上傳 (建議)
var report = AdsApp.report(
  'SELECT AdGroupId, Id, CpcBid FROM KEYWORDS_PERFORMANCE_REPORT ' +
  'WHERE TopImpressionPercentage > 0.4 DURING LAST_MONTH');

var upload = AdsApp.bulkUploads().newCsvUpload([
  report.getColumnHeader('AdGroupId').getBulkUploadColumnName(),
  report.getColumnHeader('Id').getBulkUploadColumnName(),
  report.getColumnHeader('CpcBid').getBulkUploadColumnName()]);
upload.forCampaignManagement();

var reportRows = report.rows();
while (reportRows.hasNext()) {
  var row = reportRows.next();
  row['CpcBid'] = row['CpcBid'] + 0.02;
  upload.append(row.formatForUpload());
}

upload.apply();
按 ID 選取及更新關鍵字 (較不理想)
var reportRows = AdsApp.report('SELECT AdGroupId, Id, CpcBid FROM ' +
    'KEYWORDS_PERFORMANCE_REPORT WHERE TopImpressionPercentage > 0.4 ' +
    ' DURING LAST_MONTH')
    .rows();

var map = {
};

while (reportRows.hasNext()) {
  var row = reportRows.next();
  var adGroupId = row['AdGroupId'];
  var id = row['Id'];

  if (map[adGroupId] == null) {
    map[adGroupId] = [];
  }
  map[adGroupId].push([adGroupId, id]);
}

for (var key in map) {
  var keywords = AdsApp.keywords()
      .withCondition('AdGroupId="' + key + '"')
      .withIds(map[key])
      .get();

  while (keywords.hasNext()) {
    var keyword = keywords.next();
    keyword.bidding().setCpc(keyword.bidding().getCpc() + 0.02);
  }
}

雖然第二種做法的成效很不錯,但第一種做法 會成為首選

  • Google Ads 指令碼對單次執行作業中可擷取或更新的物件數量設有限制,而第二種方法中的選取和更新作業會計入該限制。

  • 大量上傳作業的實體更新數量和整體執行時間上限較高。

按廣告活動分組大量上傳資料

建立大量上傳試算表時,請嘗試依父項廣告活動分組作業。這麼做不僅能提高效率,也能降低發生衝突變更/並行錯誤的可能性。

請考慮同時執行兩個大量上傳工作。一個暫停廣告群組中的廣告,另一個調整關鍵字出價。即使作業無關 這些作業可能適用於同一個廣告群組內的實體 (也可以是兩個不同的 同一個廣告活動中的廣告群組)。發生這種情況時,系統會鎖定父項實體 (共用廣告群組或廣告活動),導致大量上傳工作任務互相阻斷。

Google Ads 指令碼在單一大量上傳工作內可以最佳化執行作業,因此 最簡單的方法就是在每個帳戶 時間。如果您決定為每個帳戶執行多個大量上傳作業,請務必確保大量上傳作業會在互斥的廣告活動清單 (以及子實體) 中執行,以便發揮最佳成效。

報表

運用報表擷取統計資料

如要擷取大量實體及其統計資料,通常建議使用報表,而非標準 AdsApp 方法。建議使用報表,原因如下:

  • 報表可讓您在處理大型查詢時提高成效。
  • 報表不會達到正常擷取配額。

請比較下列程式碼片段,這段程式碼會擷取上個月獲得超過 50 次點擊的所有關鍵字的點擊次數、曝光次數、費用和文字:

程式設計方法 程式碼片段
使用報表 (建議做法)
  report = AdsApp.search(
      'SELECT ' +
      '   ad_group_criterion.keyword.text, ' +
      '   metrics.clicks, ' +
      '   metrics.cost_micros, ' +
      '   metrics.impressions ' +
      'FROM ' +
      '   keyword_view ' +
      'WHERE ' +
      '   segments.date DURING LAST_MONTH ' +
      '   AND metrics.clicks > 50');
  while (report.hasNext()) {
    var row = report.next();
    Logger.log('Keyword: %s Impressions: %s ' +
        'Clicks: %s Cost: %s',
        row.adGroupCriterion.keyword.text,
        row.metrics.impressions,
        row.metrics.clicks,
        row.metrics.cost);
  }
使用 AdsApp 疊代器 (不建議)
var keywords = AdsApp.keywords()
    .withCondition('metrics.clicks > 50')
    .forDateRange('LAST_MONTH')
    .get();
while (keywords.hasNext()) {
  var keyword = keywords.next();
  var stats = keyword.getStatsFor('LAST_MONTH');
  Logger.log('Keyword: %s Impressions: %s ' +
      'Clicks: %s Cost: %s',
      keyword.getText(),
      stats.getImpressions(),
      stats.getClicks(),
      stats.getCost());
}

第二種方法不在於關鍵字上,因此不建議採用 並一次擷取一個實體的統計資料。在 因為它會擷取單一呼叫中的所有資料,並做為串流方式 這通常代表交易 不會十分要求關聯語意此外,在第二個方法中擷取的關鍵字是 將計入您指令碼的配額中,使用 get() 呼叫。

改用搜尋功能,而非報表

這是專為舊基礎架構打造的報表方法, 格式提高。這表示它必須轉換查詢結果,以符合舊版樣式,但並非所有欄位都支援舊版樣式,而且會為每個呼叫增加額外負擔。

建議您改用搜尋功能,充分運用新版 Google Ads API 報表的所有功能。

使用 GAQL 改用 AWQL

雖然報表查詢和 withCondition 呼叫仍支援 AWQL,但會透過轉譯層執行,而轉譯層與真正的 AWQL 不完全相容。如要完全控管查詢,請務必使用 GAQL

如果您有現有的 AWQL 查詢需要轉換,可以使用查詢遷移工具

請勿選取超過所需的資料列

報表 (和選擇器) 執行速度取決於 系統會傳回多個資料列,無論您 反覆改進因此請一律使用特定篩選器 盡可能根據您的用途 盡可能減少結果集

舉例來說,假設您想找出出價超出特定範圍的廣告群組,產生兩個不同的查詢 (一個用於出價) 出價低於最低門檻,另一個出價高於頂端門檻, 系統會擷取所有廣告群組,並忽略不需要的廣告群組

程式設計方法 程式碼片段
使用兩個查詢 (建議做法)
var adGroups = []
var report = AdsApp.search(
    'SELECT ad_group.name, ad_group.cpc_bid_micros' +
    ' FROM ad_group WHERE ad_group.cpc_bid_micros < 1000000');

while (report.hasNext()) {
  var row = report.next();
  adGroups.push(row.adGroup);
}
var report = AdsApp.search(
    'SELECT ad_group.name, ad_group.cpc_bid_micros' +
    ' FROM ad_group WHERE ad_group.cpc_bid_micros > 2000000');

while (report.hasNext()) {
  var row = report.next();
  adGroups.push(row.adGroup);
}
從一般查詢中篩除 (不建議)
var adGroups = []
var report = AdsApp.search(
    'SELECT ad_group.name, ad_group.cpc_bid_micros' +
    ' FROM ad_group');

while (report.hasNext()) {
  var row = report.next();
  var cpcBidMicros = row.adGroup.cpcBidMicros;
  if (cpcBidMicros < 1000000 || cpcBidMicros > 2000000) {
    adGroups.push(row.adGroup);
  }
}

Ad Manager (我的客戶中心) 指令碼

優先執行 runInParallel 而非序列執行

為管理員帳戶編寫指令碼時,請盡可能使用 executeInParallel() 而非逐一執行。executeInParallel() 可為指令碼提供更多處理時間 (最多一小時),且每個處理的帳戶最多可處理 30 分鐘 (而非串列執行的 30 分鐘)。查看我們的限制 網頁

試算表

更新試算表時使用批次操作

更新試算表時,請盡量使用批次作業方法 (例如 getRange()),而非一次更新一個儲存格的其他方法。

請考慮以下程式碼片段,可針對 試算表。

程式設計方法 程式碼片段
在單一呼叫中更新一系列儲存格 (建議做法)
var colors = new Array(100);
for (var y = 0; y < 100; y++) {
  xcoord = xmin;
  colors[y] = new Array(100);
  for (var x = 0; x < 100; x++) {
    colors[y][x] = getColor_(xcoord, ycoord);
    xcoord += xincrement;
  }
  ycoord -= yincrement;
}
sheet.getRange(1, 1, 100, 100).setBackgroundColors(colors);
一次更新一個儲存格 (不建議使用)
var cell = sheet.getRange('a1');
for (var y = 0; y < 100; y++) {
  xcoord = xmin;
  for (var x = 0; x < 100; x++) {
    var c = getColor_(xcoord, ycoord);
    cell.offset(y, x).setBackgroundColor(c);
    xcoord += xincrement;
  }
  ycoord -= yincrement;
  SpreadsheetApp.flush();
}

Google 試算表會透過快取 所以仍然成效不如第一次程式碼片段 以及呼叫的 API 呼叫次數