このページでは、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 で AdGroup を取得する次のコード スニペットについて考えてみましょう。親キャンペーン 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 でフィルタする非常に長い GAQL クエリを作成することで、この処理を実現します。このアプローチは、エンティティの数が限られている場合は問題なく機能します。ただし、クエリの長さが増加すると、次の 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() を呼び出すため、バッチ処理が可能です。
更新が大規模な場合は一括アップロードを検討する
デベロッパーがよく行うタスクは、レポートを実行し、現在のパフォーマンス値に基づいてエンティティ プロパティ(キーワードの入札単価など)を更新することです。多数のエンティティを更新する必要がある場合は、一括アップロードの方がパフォーマンスが向上する傾向があります。たとえば、先月の TopImpressionPercentage > 0.4 が 100 を超えるキーワードの最大クリック単価を引き上げる次のスクリプトについて考えてみましょう。
| コーディングの手法 | コード スニペット |
|---|---|
| 一括アップロードを使用する(推奨) |
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() 呼び出しを使用して取得したエンティティの数に関するスクリプトの割り当てにカウントされます。
レポートの代わりに検索を使用する
レポート メソッドは古いインフラストラクチャ用に構築されたもので、GAQL を使用している場合でも、結果はフラット形式で出力されます。つまり、クエリの結果を古いスタイルに一致するように変換する必要があります。これはすべてのフィールドでサポートされているわけではなく、各呼び出しにオーバーヘッドが追加されます。
新しい Google Ads API レポートのすべての機能を活用するには、検索を使用することをおすすめします。
AWQL よりも GAQL を優先する
AWQL はレポートクエリと withCondition 呼び出しでは技術的にはまだ機能しますが、推奨されません。クエリを完全に制御するには、代わりに GAQL を使用してください。
必要な行数を超えて選択しない
レポート(とセレクタ)の実行速度は、レポートによって返される行の合計数に基づきます。それらを反復処理するかどうかは関係ありません。つまり、ユースケースに合わせて結果セットをできるだけ最小限に抑えるために、常に特定のフィルタを使用する必要があります。
たとえば、特定の範囲外の入札単価が設定されている広告グループを探す場合、手順は次のとおりです。すべての広告グループを取得して、対象外の広告グループを無視するよりも、下限しきい値を下回る入札単価と上限しきい値を上回る入札単価の 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 呼び出しの数が多いため、最初のスニペットと比較してパフォーマンスが低下します。