Рекомендации

На этой странице приведены рекомендации по разработке скриптов Google Рекламы.

Селекторы

Фильтрация с помощью селекторов

По возможности используйте фильтры, чтобы запрашивать только нужные объекты. Применение правильных фильтров дает следующие преимущества:

  • Код значительно проще.
  • Скрипт будет работать быстрее.

Сравните следующие фрагменты кода:

Метод кодирования Фрагмент кода
Как отфильтровать файлы с помощью селекторов (рекомендуется)
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.
    }
  }
}

Второй подход не рекомендуется, поскольку он перебирает иерархию кампаний и групп объявлений в вашем аккаунте, хотя вам нужен только выбранный набор объявлений и их родительские кампании и группы объявлений. Первый подход ограничивает итерацию списком объявлений, применяя к селектору определенный фильтр для родительских объектов.

По возможности используйте для фильтрации идентификаторы

При фильтрации объектов лучше использовать их идентификаторы, а не другие поля.

Рассмотрим следующие фрагменты кода, которые выбирают кампанию.

Метод кодирования Фрагмент кода
Фильтрация по идентификатору (рекомендуется)
var campaign = AdsApp.campaigns()
    .withIds([12345])
    .get()
    .next();
Фильтрация по названию (менее оптимальный вариант)
var campaign = AdsApp.campaigns()
    .withCondition('Name="foo"')
    .get()
    .next();

Второй подход менее оптимален, поскольку фильтрация выполняется по полю, не содержащему идентификатор.

По возможности фильтруйте по родительским идентификаторам.

При выборе объекта по возможности используйте фильтр по родительским идентификаторам. Это позволит ускорить выполнение запросов, ограничив список объектов, которые извлекаются серверами при фильтрации результатов.

Рассмотрим следующий фрагмент кода, который извлекает AdGroup по ее идентификатору. Предположим, что идентификатор родительской кампании известен.

Метод кодирования Фрагмент кода
Фильтрация по идентификаторам кампаний и групп объявлений (рекомендуется)
var adGroup = AdsApp.adGroups()
    .withIds([12345])
    .withCondition('CampaignId="54678"')
    .get()
    .next();
Фильтрация только по идентификатору группы объявлений (менее оптимальный вариант)
var adGroup = AdsApp.adGroups()
    .withIds([12345])
    .get()
    .next();

Хотя оба фрагмента кода дают одинаковые результаты, дополнительная фильтрация во фрагменте 1 с использованием идентификатора родительского элемента (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

При работе со скриптами часто требуется создать отчет по списку объектов. Обычно разработчики делают это, создавая очень длинный запрос GAQL, который фильтрует идентификаторы объектов с помощью предложения IN. Этот подход хорошо работает, когда количество объектов ограничено. Однако по мере увеличения длины запроса производительность скрипта снижается по двум причинам:

  • Чем длиннее запрос, тем больше времени требуется на его обработку.
  • Каждый идентификатор, добавленный в условие IN, – это дополнительное условие для оценки, поэтому на его обработку требуется больше времени.

В таких случаях лучше применить ярлык к объектам, а затем отфильтровать их по 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 Рекламы, скрипты Google Рекламы не выполняют их немедленно. Вместо этого он пытается объединить несколько изменений в пакеты, чтобы отправить один запрос, который выполнит несколько изменений. Это позволяет ускорить работу скриптов и снизить нагрузку на серверы Google Рекламы. Однако некоторые шаблоны кода заставляют скрипты Google Рекламы часто очищать пакет операций, что приводит к замедлению работы скрипта.

Ниже приведен скрипт, который обновляет ставки для списка ключевых слов.

Метод кодирования Фрагмент кода
Отслеживайте обновленные элементы (рекомендуется)
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 Рекламы очищать операцию setCpc() и выполнять только одну операцию за раз. Первый подход, хотя и похож на второй, имеет дополнительное преимущество: он поддерживает пакетную обработку, поскольку вызов getCpc() выполняется в отдельном цикле от того, в котором вызывается setCpc().

По возможности используйте конструкторы

Скрипты Google Рекламы поддерживают два способа создания объектов: конструкторы и методы создания. Конструкторы более гибкие, чем методы создания, поскольку они предоставляют доступ к объекту, созданному на основе вызова 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 Рекламы не смогут выполнять пакетные операции.

Рассмотрим фрагменты кода, которые создают список ключевых слов и выводят идентификаторы новых ключевых слов.

Метод кодирования Фрагмент кода
Отслеживайте обновленные элементы (рекомендуется)
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 Рекламы выполнять операции по одной. Первый подход, хотя и похож на второй, позволяет выполнять пакетную обработку, поскольку мы вызываем operation.getResult() в цикле, отличном от того, в котором он был создан.

Для больших изменений используйте массовую загрузку

Разработчики часто выполняют такие задачи, как создание отчетов и обновление свойств объектов (например, ставок на уровне ключевого слова) на основе текущих показателей эффективности. Если вам нужно обновить большое количество объектов, массовая загрузка обычно обеспечивает более высокую производительность. Например, рассмотрим следующие скрипты, которые увеличивают максимальную цену за клик ключевых слов, у которых TopImpressionPercentage > 0.4 за последний месяц:

Метод кодирования Фрагмент кода
Массовая загрузка (рекомендуется)

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();
Как выбрать и изменить ключевые слова по идентификатору (менее оптимальный вариант)
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 Рекламы есть ограничение на количество объектов, которые можно получить или обновить за один запуск. Операции выбора и обновления во втором подходе учитываются в этом ограничении.

  • При массовой загрузке лимиты выше как по количеству объектов, которые можно обновить, так и по общему времени выполнения.

Группируйте массовые загрузки по кампаниям

При создании массовых загрузок старайтесь группировать операции по родительской кампании. Это повышает эффективность и снижает вероятность конфликтующих изменений и ошибок параллелизма.

Предположим, что параллельно выполняются две задачи массовой загрузки. Один приостанавливает показ объявлений в группе, а другой корректирует ставки для ключевых слов. Даже если операции не связаны, они могут применяться к объектам в одной группе объявлений (или в двух разных группах объявлений в одной кампании). В этом случае система заблокирует родительский объект (общую группу объявлений или кампанию), что приведет к блокировке задач массовой загрузки.

Скрипты Google Рекламы могут оптимизировать выполнение в рамках одной задачи массовой загрузки, поэтому проще всего запускать только одну задачу массовой загрузки для аккаунта за раз. Если вы решите запустить несколько массовых загрузок для одного аккаунта, убедитесь, что они работают с взаимоисключающими списками кампаний (и их дочерних объектов). Это позволит обеспечить оптимальную производительность.

Отчеты

Как использовать отчеты для получения статистики

Если вам нужно получить большое количество объектов и их статистику, часто лучше использовать отчеты, а не стандартные методы 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().

Используйте поиск вместо отчета

Метод отчета был создан для старой инфраструктуры и будет выводить результаты в плоском формате, даже если вы используете GAQL. Это означает, что результаты запроса должны быть преобразованы в соответствии со старым стилем, который поддерживается не для всех полей и увеличивает нагрузку на каждый вызов.

Рекомендуем использовать метод search, чтобы получить доступ ко всем функциям отчетов в новом Google Ads API.

Предпочитайте GAQL AWQL

Хотя AWQL технически по-прежнему работает для запросов отчетов и вызовов withCondition, мы не рекомендуем его использовать. Чтобы полностью контролировать запросы, используйте GAQL.

Не выбирайте больше строк, чем вам нужно

Скорость выполнения отчетов (и селекторов) зависит от общего количества строк, которые будут возвращены отчетом, независимо от того, выполняете ли вы итерацию по ним. Это означает, что вы всегда должны использовать определенные фильтры, чтобы максимально минимизировать набор результатов в соответствии с вашим вариантом использования.

Например, вы хотите найти группы объявлений со ставками, выходящими за определенные пределы. Быстрее будет выполнить два отдельных запроса: один для ставок ниже нижнего порога, а другой – для ставок выше верхнего порога, чем получить все группы объявлений и игнорировать те, которые вас не интересуют.

Метод кодирования Фрагмент кода
Используйте два запроса (рекомендуется)
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);
  }
}

Скрипты Центра клиентов (MCC) Google Рекламы

Используйте executeInParallel вместо последовательного выполнения

При написании скриптов для управляющих аккаунтов по возможности используйте 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.