要提高腳本和平台的性能,請查看並遵循下面概述的最佳實踐。
使用選取器
使用篩選條件
使用選取器的篩選,而不是自己篩選實體。 選取器可讓您依識別碼和條件進行篩選。 例如,您可以依實體的成效 (平均點選成本大於 10) 的傳回廣告活動、其狀態 (已暫停) 的廣告活動、實體父物件的名稱等進行篩選。
使用篩選的優點:
正確的方式
var adGroups = AdsApp.adGroups()
.withCondition('Status = PAUSED')
.get();
while (adGroups.hasNext()) {
var adGroup = adGroups.next();
// Do something with paused ad group.
}
錯誤的方式
var adGroups = AdsApp.adGroups().get();
while (adGroups.hasNext()) {
var adGroup = adGroups.next();
if (adGroup.isPaused() == true) {
// Do something with paused ad group.
}
}
不要周遊實體階層
如果您想要取得實體的子實體或實體的父實體,請勿周遊實體階層來取得它們。
若要取得子實體,請在您想要的層級使用子實體的集合。
正確的方式
// Get all ads.
var ads = AdsApp.ads().get();
while (ads.hasNext()) {
var ad = ads.next();
// Do something with ad.
}
或者,如果您想要特定廣告活動中的廣告:
// Get all ads in the campaign, 'mycampaign'.
var ads = AdsApp.ads()
.withCondition("CampaignName = 'mycampaign'")
.get();
while (ads.hasNext()) {
var ad = ads.next();
// Do something with ad.
}
或者,如果您有廣告活動物件,則可取得廣告活動的廣告:
// Get all ads in the campaign.
var ads = campaign.ads().get();
while (ads.hasNext()) {
var ad = ads.next();
// Do something with ad.
}
錯誤的方式
var campaigns = AdsApp.campaigns().get();
while (campaigns.hasNext()) {
var adGroups = campaigns.next().adGroups().get();
while (adGroups.hasNext()) {
var ads = adGroups.next().ads().get();
while (ads.hasNext()) {
var ad = ads.next();
// Do something with ad.
}
}
}
如果您想要取得實體的父系,這同樣適用。 使用子實體的父存取子方法,而不是周遊階層來取得父項。
正確的方式
// Get all ads.
var ads = AdsApp.ads()
.withCondition('Clicks > 5')
.forDateRange('LAST_7_DAYS')
.get();
while (ads.hasNext()) {
var ad = ads.next();
// Do something with campaign and adGroup.
var adGroup = ad.adGroup();
var campaign = ad.campaign();
}
錯誤的方式
var campaigns = AdsApp.campaigns().get();
while (campaigns.hasNext()) {
var campaign = campaigns.next();
var adGroups = campaign.adGroups().get();
while (adGroups.hasNext()) {
var adGroup = adGroups.next();
var ads = adGroup.ads().get();
while (ads.hasNext()) {
var ad = ads.next();
if ('<some condition is met>') {
// Do something with campaign and adGroup.
}
}
}
}
盡可能使用實體 ID
使用 ID 來篩選實體可提供最佳效能。
此
var adGroups = AdsApp.adGroups()
.withIds(["123456"])
.get();
while (adGroups.hasNext()) {
var adGroup = adGroups.next();
// Do something with adGroup.
}
提供比這更好的效能
var adGroups = AdsApp.adGroups()
.withCondition("Name = 'myadgroup'")
.get();
while (adGroups.hasNext()) {
var adGroup = adGroups.next();
// Do something with adGroup.
}
避免選擇器和不必要的獲取次數的緊密循環
避免因取得單一實體的 Get 要求而發生迴圈。 例如,假設您執行關鍵字廣告成效報表,並想要更新報表中的關鍵字。 在迴圈報表中的每一列時,您應該建立一份關鍵字 ID 清單,而不是從報表取得資料列、取得關鍵字,然後再加以更新。 然後,將識別碼清單傳遞至選取器,以在單一取得要求中取得所有關鍵字。 然後,您可以反覆瀏覽關鍵字清單並更新它們。
正確的方式
var report = AdsApp.report('<report query goes here>');
var rows = report.rows();
var idLists = []; // an array where each element contains an array of IDs.
var idList = []; // array of IDs that's limited to maxCount.
var maxCount = 10000;
while (rows.hasNext()) {
var row = rows.next();
if (idList.length < maxCount) {
idList.push(row['id']);
}
else {
idLists.push(idList);
idList = [];
}
}
for (idList of idLists) {
var keywords = AdsApp.keywords()
.withIds(idList)
.get();
while (keywords.hasNext()) {
var keyword = keywords.next();
// update the keyword
}
}
錯誤的方式
var report = AdsApp.report('<report query goes here>');
var rows = report.rows();
while (rows.hasNext()) {
var row = rows.next();
var keyword = AdsApp.keywords()
.withIds([row['id']])
.get()
.next();
// update the keyword
}
只有在您打算呼叫實體的 getStats 方法時,才包含 forDateRange 方法
呼叫選取器的方法 forDateRange 會導致選取器取得實體的效能資料。 取得實體的效能資料非常昂貴,因此只有在您打算呼叫實體的方法 getStats 並使用資料時,才能取得它。
您為實體指定的日期範圍不適用於您從該實體存取的父實體或子實體。 例如,如果您取得某個廣告群組,然後取得其上層廣告活動,並嘗試存取該廣告活動的成效計量,則呼叫會失敗。
campaignStats.getReturnOnAdSpend()下列範例中的呼叫失敗,因為日期範圍適用於廣告群組,而非廣告活動。
var myAdGroups = AdsApp.adGroups().
.withCondition("CampaignName CONTAINS 'gen'")
.forDateRange("LAST_7_DAYS")
.get();
while (myAdGroups.hasNext()) {
var adGroup = myAdGroups.next();
var campaign = adGroup.getCampaign();
var campaignStats = campaign.getStats();
var campaignROAS = campaignStats.getReturnOnAdSpend();
}
若要讓這項功能運作,您必須為廣告活動建立選取器。
var myAdGroups = AdsApp.adGroups().
.withCondition("CampaignName CONTAINS 'gen'")
.forDateRange("LAST_7_DAYS")
.get();
while (myAdGroups.hasNext()) {
var adGroup = myAdGroups.next();
var campaign = AdsApp.campaigns()
.withIds([adGroup.getCampaign().getId()])
.forDateRange("LAST_7_DAYS")
.get()
.next();
var campaignStats = campaign.getStats();
var campaignROAS = campaignStats.getReturnOnAdSpend();
}
請勿變更在選取器中做為條件的實體屬性
迭代器透過一次只載入單一項目而不是整組項目來減輕記憶體壓力。 因此,變更您在選取器中用作條件的屬性可能會導致非預期的行為。
正確的方式
var adGroups = [];
var iterator = AdsApp.adGroups()
.withCondition('Status = ENABLED')
.get();
while (iterator.hasNext()) {
adGroups.push(iterator.next());
}
for (var adGroup of adGroups) {
adGroup.pause();
}
錯誤的方式
var adGroups = AdsApp.adGroups()
.withCondition('Status = ENABLED')
.get();
while (adGroups.hasNext()) {
var adGroup = adGroups.next();
adGroup.pause();
}
批次更新
為了改善效能,指令碼會批次處理建置要求。 如果您呼叫建置請求的作業方法,它會強制指令碼立即處理佇列的建置請求,從而抵消任何效能提升。 如果您要建立多個實體,請勿在用於建置實體的相同迴圈中執行作業方法。 這會導致效能不佳,因為一次只處理一個實體。 相反地,請建立作業陣列,並在建置迴圈之後處理它們。
正確的方式
// An array to hold the operations, so you
// can process them after all the entities are queued.
var operations = [];
// Create all the new entities.
for (var i = 0; i < keywords.length; i++) {
var keywordOperation = AdsApp.adGroups().get().next()
.newKeywordBuilder()
.withText(keywords[i])
.build();
operations.push(keywordOperation);
}
// Now call the operation method so the build requests
// get processed in batches.
for (var i = 0; i < operations.length; i++) {
var newKeyword = operations[i].getResult();
}
錯誤的方式
for (var i = 0; i < keywords.length; i++) {
var keywordOperation = AdsApp.adGroups().get().next() // Get the first ad group
.newKeywordBuilder() // Add the keyword to the ad group
.withText(keywords[i])
.build();
// Don't get results in the same loop that creates
// the entity because Scripts then only processes one
// entity at a time.
var newKeyword = keywordOperation.getResult();
}
如果您更新實體,然後取得已更新的相同屬性,也是如此。 切莫這麼做:
var bidAmount = 1.2;
while (keywords.hasNext()) {
var keyword = keywords.next();
keyword.bidding().setCpc(bidAmount);
if (keyword.bidding().getCpc() != bidAmount) {
Logger.log(`Failed to update bid amount for keyword, ${keyword.getText()} (${keyword.getId()})`);
}
}
取得大型實體集時使用 yield 關鍵字
擷取大量實體並將它們載入到循環中處理的單一清單有幾個缺點:
視要求的大小而定,在迴圈開始之前,可能需要 n 個後端要求才能擷取所有實體。 如果您不處理所有內容,那麼用於獲取未處理實體的時間和計算能力就會被浪費。 例如,如果您檢索了 10K 關鍵字,並且只處理了 2K 關鍵字後循環中斷,則浪費了用於獲取剩餘 8K 關鍵字的時間和算力。
建立清單需要更多記憶體來同時容納所有實體。
若要解決這些問題,請使用 yield 關鍵字,它可讓您的指令碼隨選擷取實體,或者在某種意義上,僅在需要時「延遲」擷取實體。 這表示您的指令碼不會進行超過目前需要的呼叫,也不會傳遞大型物件清單。
此範例包含記錄,以說明使用 yield 關鍵字時的控制流程。
function main() {
const keywords = getKeywords();
//@ts-ignore <-- suppresses iterator error
for (const keyword of keywords) {
Logger.log("in for loop\n\n");
}
}
// Note that you must use the yield keyword in a generator function - see the
// '*' at the end of the function keyword.
function* getKeywords() {
const keywords = AdsApp.keywords()
.withCondition("Status = ENABLED")
.withCondition("CombinedApprovalStatus = APPROVED")
.withLimit(10)
.get();
Logger.log(`total keywords in account: ${keywords.totalNumEntities()} \n\n`);
while (keywords.hasNext()) {
Logger.log("before next()\n\n");
yield keywords.next();
Logger.log("after next\n\n");
}
}
避免實體限制的呼叫模式
指令碼可以為帳戶傳回的實體數量有限制。 如果要求傳回超過此限制,指令碼會擲回錯誤,並顯示訊息「 實體太多」。 下列範例顯示取得大量實體時應使用的呼叫模式。 此範例會先嘗試在帳戶層級擷取所有關鍵字。 如果失敗,它會嘗試按廣告活動進行多次呼叫以擷取關鍵字,因為廣告活動層級的實體通常較少。 如果需要,您通常可以將此模式持續到廣告群組層級。
如需使用 yield 關鍵字的相關資訊,請參閱在 取得大型實體集時使用 yield 關鍵字。
function* getEntities() {
const applyConditions = _ => _
.withCondition('CampaignStatus = ENABLED')
.withCondition('AdGroupStatus = ENABLED')
.withCondition('Status = ENABLED')
.withCondition("CombinedApprovalStatus = DISAPPROVED");
try {
// Get the account's keywords.
const keywords = applyConditions(AdsApp.keywords()).get();
while (keywords.hasNext()) {
yield keywords.next();
}
} catch (e) {
if (!e.message.startsWith('There are too many entities')) {
throw e;
}
// If there are too many keywords at the account level,
// get keywords by campaigns under the account.
const campaigns = AdsApp.campaigns().get();
while (campaigns.hasNext()) {
const campaign = campaigns.next();
const keywords = applyConditions(campaign.keywords()).get();
while (keywords.hasNext()) {
yield keywords.next();
}
}
}
}