このページでは、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. } } |
2 つ目の方法は、アカウント内のすべてのキーワードのリストを取得して、そのリストにフィルタを適用するだけなので、おすすめしません。
キャンペーンの階層構造の順次処理を避ける
特定のレベルでエンティティを取得する場合は、キャンペーン階層全体を走査するのではなく、そのレベルのコレクション メソッドを使用します。これにより、シンプルになるだけでなく、パフォーマンスも大幅に向上します。システムがすべてのキャンペーンと広告グループを不必要に読み込む必要がなくなります。
アカウント内のすべての広告を取得する次のコード スニペットを比較します。
コーディングの手法 | コード スニペット |
---|---|
適切な収集方法を使用する(推奨) | 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. } } |
2 つ目の方法は、広告のみが必要な一方で、オブジェクトの階層全体(キャンペーン、広告グループ)を取得しようとするため、推奨されません。
特定の親アクセサ メソッドを使う
取得済みオブジェクトの親エンティティを取得する必要がある場合は、この場合、階層全体を取得するのではなく、提供されたアクセサラ メソッドを使用する必要があります。
先月 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. } } } |
2 つ目の方法は、アカウント内のキャンペーンと広告グループの階層全体を取得するため、広告セットに関連付けられているキャンペーンと広告グループのサブセットのみが必要である場合におすすめしません。最初のアプローチでは、関連する広告コレクションのみを取得し、適切なメソッドを使用して親オブジェクトにアクセスします。
特定の親フィルタを使う
特定のキャンペーンまたは広告グループ内のエンティティにアクセスするには、取得してから階層を移動するのではなく、セレクタで特定のフィルタを使用します。
指定したキャンペーンと広告グループ内のテキスト広告のリストを取得し、前月に 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. } } } |
2 つ目の方法は、アカウント内のキャンペーンと広告グループの階層を反復処理するため、選択した広告セットとその親のキャンペーンと広告グループのみが必要である場合におすすめしません。最初のアプローチでは、セレクタに親エンティティの特定のフィルタを適用することで、反復処理を広告リストに制限します。
可能な場合はフィルタに ID を使う
エンティティをフィルタする場合は、他のフィールドではなく ID でエンティティをフィルタすることをおすすめします。
キャンペーンを選択する以下のコード スニペットを考えてみます。
コーディングのアプローチ | コード スニペット |
---|---|
ID でフィルタする(推奨) | var campaign = AdsApp.campaigns() .withIds([12345]) .get() .next(); |
名前でフィルタする(最適ではない) | var campaign = AdsApp.campaigns() .withCondition('Name="foo"') .get() .next(); |
ID ではないフィールドでフィルタしている後者のアプローチは、可能であれば避けてください。
可能な場合は常に親 ID でフィルタする
エンティティを選択する場合、可能であれば常に親 ID でフィルタしてください。これにより、結果のフィルタリング時にサーバーが取得するエンティティのリストが制限され、クエリの速度が向上します。
次のコード スニペットは、ID で広告グループを取得します。親キャンペーン ID が既知であることを前提としています。
コーディングの手法 | コード スニペット |
---|---|
キャンペーン 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. } |
どちらのコード スニペットでもパフォーマンスは同程度ですが、2 つ目のアプローチでは、セレクタの条件数が増えるにつれてコードが複雑になる傾向があります。また、新しいエンティティを追加するようにスクリプトを編集するよりも、新しいエンティティにラベルを適用する方が簡単です。
IN 句に含める条件の数を制限する
スクリプトを実行する場合の一般的なユースケースは、エンティティのリストのレポートを実行することです。通常、デベロッパーは、IN 句を使用してエンティティ ID でフィルタする非常に長い AWQL クエリを作成することで、この処理を行います。このアプローチは、エンティティの数が限られている場合に適しています。ただし、クエリの長さが増加すると、次の 2 つの理由でスクリプトのパフォーマンスが低下します。
- クエリが長くなると解析時間も長くなる。
- 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 広告のエンティティに変更を加えても、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()); } |
2 つ目のアプローチは、keyword.bidding().getCpc()
の呼び出しにより Google 広告スクリプトが setCpc()
オペレーションを強制的にフラッシュし、一度に 1 つのオペレーションしか実行されないため、おすすめしません。最初のアプローチは 2 つ目のアプローチと似ていますが、getCpc()
呼び出しが setCpc()
が呼び出されるループとは別のループで行われるため、バッチ処理をサポートするという利点があります。
可能な場合はビルダーを使う
Google 広告スクリプトでは、新しいオブジェクトを作成するための 2 つの方法(ビルダーと作成メソッド)がサポートされています。ビルダーは、API 呼び出しから作成されたオブジェクトにアクセスできるため、作成メソッドよりも柔軟性があります。
以下のコード スニペットを考えてみます。
コーディングのアプローチ | コード スニペット |
---|---|
ビルダーを使用する(推奨) | var operation = adGroup.newKeywordBuilder() .withText('shoes') .build(); var keyword = operation.getResult(); |
作成方法を使用する(非推奨) | adGroup.createKeyword('shoes'); var keyword = adGroup.keywords() .withCondition('KeywordText="shoes"') .get() .next(); |
2 つ目の方法は、キーワードの取得に追加の選択オペレーションが伴うため、推奨されません。また、作成方法も非推奨になりました。
ただし、ビルダーを誤って使用すると、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()); } |
2 つ目の方法は、オペレーションを作成する同じループ内で operation.getResult()
を呼び出すため、Google 広告スクリプトが一度に 1 つのオペレーションしか実行できないため、推奨されません。最初のアプローチは類似していますが、作成されたループとは異なるループで operation.getResult() を呼び出すため、バッチ処理が可能です。
更新が大規模な場合は一括アップロードを検討する
デベロッパーが行う一般的なタスクは、レポートを実行し、現在のパフォーマンス値に基づいてエンティティ プロパティ(キーワード入札単価など)を更新することです。多数のエンティティを更新する必要がある場合は、一括アップロードのほうがパフォーマンスが向上する傾向があります。たとえば、過去 1 か月の TopImpressionPercentage > 0.4
が 100 以上のキーワードの 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); } } |
2 つ目のアプローチはパフォーマンスが非常に優れていますが、この場合は次の理由から 1 つ目のアプローチが推奨されます。
Google 広告スクリプトでは、1 回の実行で取得または更新できるオブジェクトの数に上限があります。2 つ目の方法の選択オペレーションと更新オペレーションは、その上限にカウントされます。
一括アップロードでは、更新できるエンティティの数と全体的な実行時間の両方に上限があります。
複数の一括アップロードをキャンペーン別にグループ化する
一括アップロードを作成する際は、親キャンペーンごとにオペレーションをグループ化することをおすすめします。これにより、効率が向上し、変更の競合や同時実行エラーの発生可能性が低くなります。
2 つの一括アップロード タスクを並列で実行する場合を考えてみます。1 つは広告グループ内の広告を一時停止し、もう 1 つはキーワードの入札単価を調整します。オペレーションは関連性がなくても、同じ広告グループ(または同じキャンペーンの 2 つの異なる広告グループ)のエンティティに適用される場合があります。この場合、親エンティティ(共有広告グループまたはキャンペーン)がロックされ、一括アップロード タスクが互いにブロックされる原因となります。
Google 広告スクリプトでは、1 つの一括アップロード タスク内で実行を最適化できるため、最も簡単な方法は、アカウントごとに一度に 1 つの一括アップロード タスクのみを実行することです。アカウントごとに複数の一括アップロードを実行する場合は、パフォーマンスを最適化するために、一括アップロードが相互に排他的なキャンペーン リスト(およびその子エンティティ)で実行されるようにしてください。
レポート
レポートを使って統計情報を取得する
大量のエンティティとその統計情報を取得する場合は、標準の 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()); } |
2 番目の方法は、キーワードを反復処理してエンティティを 1 つずつ取得するため、おすすめしません。この場合、レポートは 1 回の呼び出しですべてのデータを取得し、必要に応じてストリーミングするため、パフォーマンスが向上します。また、2 番目の方法で取得されたキーワードは、get()
呼び出しを使用して取得されたエンティティ数のスクリプトの割り当てにカウントされます。
レポートではなく検索を使用する
report メソッドは古いインフラストラクチャ用に構築されており、GAQL を使用している場合でも結果がフラット形式で出力されます。つまり、クエリの結果を変換して古いスタイルに合わせる必要があります。これはすべてのフィールドでサポートされているわけではなく、各呼び出しにオーバーヘッドが発生します。
検索を使用すると、新しい Google 広告 API レポートのすべての機能を利用できます。
AWQL よりも GAQL を優先する
AWQL は引き続きレポートクエリと withCondition
呼び出しでサポートされていますが、真の AWQL と完全に互換性のない変換レイヤを介して実行されます。クエリを完全に制御するには、GAQL を使用していることを確認してください。
変換する既存の AWQL クエリがある場合は、クエリ移行ツールを使用できます。
必要以上の行を選択しないでください
レポート(およびセレクタ)の実行速度は、レポートから返される行の合計数に基づいています(行を反復処理するかどうかに関係なく)。つまり、ユースケースに合わせて、常に特定のフィルタを使用して結果セットを可能な限り最小限に抑える必要があります。
たとえば、特定の範囲外の入札単価の広告グループを探す場合、すべての広告グループを取得して、関心のない広告グループを無視するよりも、下限しきい値を下回る入札単価と上限しきい値を超える入札単価の 2 つのクエリを別々に実行するほうが速くなります。
コーディングの手法 | コード スニペット |
---|---|
2 つのクエリを使用する(推奨) | 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)スクリプト
可能な限りシリアル実行ではなく executeInParallel を使う
MCC アカウントのスクリプトを記述する場合は、可能であればシリアル実行ではなく executeInParallel()
を使用します。executeInParallel()
を使用すると、スクリプトの処理時間が長くなり(最大 1 時間)、処理されるアカウントごとに最大 30 分(シリアル実行の場合は合計 30 分)の処理時間が確保されます。詳しくは、上限に関するページをご覧ください。
スプレッドシート
スプレッドシートの更新には一括操作を使う
スプレッドシートを更新する場合は、一度に 1 つのセルを更新するメソッドではなく、一括オペレーション メソッド(getRange()
など)を使用することをおすすめします。
スプレッドシートにフラクタル パターンを生成する次のコード スニペットについて考えてみましょう。
コーディングの手法 | コード スニペット |
---|---|
1 回の呼び出しでセル範囲を更新する(推奨) | 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); |
一度に 1 つのセルを更新する(非推奨) | 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 スプレッドシートは値をキャッシュに保存することで 2 つ目のコード スニペットを最適化しようとしますが、API 呼び出しの数が多いため、最初のスニペットと比較してパフォーマンスが低下します。