На этой странице рассматриваются различные передовые практики разработки с использованием скриптов 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 (рекомендуется) | var campaign = AdsApp.campaigns() .withIds([12345]) .get() .next(); |
Фильтр по имени (менее оптимально) | var campaign = AdsApp.campaigns() .withCondition('Name="foo"') .get() .next(); |
Второй подход менее оптимален, поскольку мы фильтруем по полю, не являющемуся идентификатором.
Фильтрация по родительским идентификаторам, когда это возможно
При выборе сущности по возможности фильтруйте её по родительским идентификаторам. Это ускорит выполнение запросов, ограничив список сущностей, извлекаемых серверами при фильтрации результатов.
Рассмотрим следующий фрагмент кода, который извлекает группу объявлений по её идентификатору. Предположим, что известен идентификатор родительской кампании.
Подход к кодированию | Фрагмент кода |
---|---|
Фильтровать по идентификаторам кампаний и групп объявлений (рекомендуется) | 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
При запуске скриптов часто используется для создания отчёта по списку сущностей. Разработчики обычно делают это, создавая очень длинный AWQL-запрос, который фильтрует данные по идентификаторам сущностей с помощью предложения 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 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 Ads выполнять пакетную обработку своих операций.
Рассмотрим следующие фрагменты кода, которые создают список ключевых слов и выводят идентификаторы вновь созданных ключевых слов:
Подход к кодированию | Фрагмент кода |
---|---|
Следите за обновленными элементами (рекомендуется) | 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() в другом цикле, а не в том, в котором она была создана.
Рассмотрите возможность использования массовых загрузок для крупных обновлений.
Распространенная задача разработчиков — создание отчётов и обновление свойств сущностей (например, ставок по ключевым словам) на основе текущих значений эффективности. При необходимости обновления большого количества сущностей массовая загрузка, как правило, обеспечивает более высокую эффективность. Например, рассмотрим следующие скрипты, увеличивающие MaxCpc ключевых слов, 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(); |
Выбрать и обновить ключевые слова по 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 Рекламы могут оптимизировать выполнение в рамках одной задачи массовой загрузки, поэтому проще всего запускать только одну задачу массовой загрузки для каждого аккаунта одновременно. Если вы решили запустить более одной массовой загрузки для каждого аккаунта, убедитесь, что массовые загрузки выполняются для взаимоисключающего списка кампаний (и их дочерних сущностей) для достижения оптимальной эффективности.
Отчетность
Используйте отчеты для получения статистики
Когда требуется получить большое количество сущностей и их статистику, часто лучше использовать отчёты, чем стандартные методы 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. Это означает, что ему придётся преобразовать результаты запроса в соответствии со старым стилем, который поддерживается не для всех полей и увеличивает накладные расходы при каждом вызове.
Вместо этого мы предлагаем вам воспользоваться поиском, чтобы воспользоваться всеми функциями новой отчетности API Google Ads.
Предпочитать GAQL вместо AWQL
Хотя AWQL по-прежнему поддерживается в запросах отчётов и вызовах withCondition
, он выполняется через слой трансляции, который не полностью совместим с настоящим 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); } } |
Скрипты Ads Manager (MCC)
Предпочитать 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 Spreadsheets пытается оптимизировать второй фрагмент кода путем кэширования значений, он все равно обеспечивает низкую производительность по сравнению с первым фрагментом из-за количества выполняемых вызовов API.