若要提高脚本和平台的性能,请查看并遵循下面概述的最佳做法。
使用选择器
使用筛选器
使用选择器的筛选器,而不是自行筛选实体。 选择器允许按 ID 和条件进行筛选。 例如,可以按平均 CPC 大于 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 请求的循环。 例如,假设你运行了关键字 (keyword) 性能报告,并且想要更新报表中的关键字。 在循环浏览报表的每一行时,应创建关键字 (keyword) ID 列表,而不是从报表获取行、获取关键字 (keyword) 然后更新它。 然后,将 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()})`);
}
}
获取大型实体集时使用收益率关键字 (keyword)
检索大量实体并将其加载到循环中处理的单个列表中有几个缺点:
根据请求的大小,可能需要 n 个后端请求才能在循环启动之前提取所有实体。 如果不全部处理它们,则用于获取未处理的实体的时间和计算能力将浪费。 例如,如果检索 10K 个关键字,并且仅处理 2K 个关键字后循环中断,则用于获取剩余 8K 关键字的时间和计算能力将浪费。
创建列表需要更多内存来同时容纳所有实体。
若要解决这些问题,请使用收益关键字 (keyword) ,它允许脚本按需提取实体,或者,从某种意义上说,仅在需要时才“延迟”提取实体。 这意味着脚本的调用数不会超过其目前所需的数量,并且不会传递大量对象列表。
此示例包括日志记录,以说明使用收益关键字 (keyword) 时的控制流。
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");
}
}
避免实体限制的调用模式
脚本可以为帐户返回的实体数有限制。 如果请求将返回超过此限制,则脚本会引发错误,并显示消息“ 实体过多”。 以下示例演示获取大量实体时应使用的调用模式。 该示例首先尝试在帐户级别提取所有关键字。 如果失败,它会尝试多次调用以按市场活动提取关键字,因为市场活动级别的实体通常较少。 如果需要,通常可以继续此模式到广告组级别。
有关使用收益关键字 (keyword) 的信息,请参阅在获取大型实体集时使用收益关键字 (keyword) 。
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();
}
}
}
}