Feedforce Developer Blog

フィードフォース開発者ブログ

Google Ads API 徹底入門

最近周囲からのバ美肉1圧が強くなっていてどうしようか悩んでいる id:hano_tea です、こんにちは。

早速ですが、この記事に興味を持った皆様は、おそらく Google 広告を API 経由で操作・管理している、あるいはこれからする予定がある方だと思います。

皆様御存知かとは思いますが、これまで使用されていた Adwords API は 2020年中に廃止される と予告されています

Note: v201809 is the last release of the AdWords API and will sunset in 2020. Please migrate over to its replacement as early as possible.

そのため、なるべく早いうちに Adwords API から Google Ads API へと移行しないといけません…が、 Adwords API と Google Ads API では、扱える対象や行える操作にこそ互換性があれど、 実際の使い方…コードの書き方は大幅に変わっており、とてもではないですが一朝一夕に移行できるようなものではありません。

(Google の方の話によれば、レポートのみの軽い利用でも、移行期間としてバッファ込みで 100日程度 見積もっておいたほうがいいよ、トノコト)

そもそもの扱い方も Adwords API とは大きく異なり、今までよりも気にしなければいけないことが増えています(主に gRPC, Protocol Buffers 関係で)。

本稿では、 Google Ads API を扱う上でこれまでの Adwords API と大きく変わった 「データの取得方法」 と、 これまた特徴的な機能である 「原子性を持ったリソースの作成」 の2つに焦点を絞って、 Google Ads API と上手く付き合っていく方法をご紹介します。

目次

前提: Google Ads API の叩き方

基本的な Google Ads API の使い方については、公式の Quickstart が非常にわかりやすいのでそちらを参照していただければと思います。

developers.google.com

Google Ads API では、リソースの情報の取得やレポート取得といった操作を GoogleAdsService で行うことが推奨されています。

これまで(Adwords API)では、取得したい情報、レポートの種類に応じて Service を変えてリクエストする必要がありましたが、それが単一の Service に集約されて扱いやすくなりました。以下にそれぞれの公式ドキュメントを掲載しておきますので参考にしてください。

▼ Google 広告のオブジェクトに関する情報を取得する developers.google.com

▼ Google 広告のレポートに関する情報を取得する developers.google.com

なお、従来までのリクエスト方法に似た、各リソースごとの Service からの get も用意はされていますが、Google があまり使うことを推奨していないので、そちらの話は割愛します。

GoogleAdsService を利用した情報の取得は Google Ads Query Language (以下 GAQL) という、 SQL ライクなクエリを書いてリクエストを行います。詳細は後述。

Adwords の時にも似たようなクエリ言語がありました が、異なるものです。 文法自体はどちらも SQL をベースにしているのでそこまで変わったように見えませんが、記述する内容の互換性は無いので気をつけましょう。

サンプルコード

それでは実際に Google 広告の情報を取得するためのコードを見てみましょう。Ruby での最小限のサンプルコードを以下に用意しました。

require 'google/ads/google_ads'

# 対象の Google 広告アカウントの id
customer_id = '1234567890'

client = Google::Ads::GoogleAds::GoogleAdsClient.new

# Campaign の情報を取得するだけのクエリ
search_query = <<~QUERY
SELECT
  campaign.id,
  campaign.resource_name,
  campaign.name,
  customer.id,
  customer.resource_name
FROM
  campaign
WHERE
  campaign.resource_name IS NOT NULL 
LIMIT
  10000
QUERY

ga_service = client.service(:GoogleAds)

response = ga_service.search(
  customer_id,
  search_query,
  page_size: 1000,
)

なお、公式のサンプルコードはこちら にありますので、 併せてご覧いただくと良いかもしれません。

ポイント

GAQL のところで軽く触れましたが、 SQL っぽい感じのクエリを書いて欲しい情報を取得することが出来ます。 今回はキャンペーンを取得しましたが、キャンペーンに関連する(親リソースである) Customer の情報も同時に取得できます。

それでは、GAQL の組み立て方を簡単に見ていきます。まず SELECT に取得したい情報、フィールドを列挙します。 SQL 的にはとりあえず * を指定したくなりますが * は使えないのでご注意ください。

次に FROM に対象となるリソースの種類を記述します。今回はキャンペーンの情報が欲しいので Campaign を指定します。

もし検索条件を指定したい、絞り込みたい場合は WHERE が使えます。無くても大丈夫。取得対象リソースの条件を指定していきます。

LIMIT もオプショナルな値です。件数を絞りたいときに任意の整数値をセットしてください。

このあたりの文法についての詳しい解説はこちらの公式リファレンスをどうぞ。 基本的には SELECT, FROM, WHERE が使えれば良さそうな気がします。今回書いてないですが ORDER で並び替えもできます。

必要最低限の情報を取得するようにクエリを組み立てる

SELECT のところで軽く触れた通り、 * のように雑にフィールドを指定することはできません。 基本的に「必要最低限のフィールドだけを指定する」ことを Google が推奨しているので、それに従いましょう。

必要最低限の情報だけ扱う、という方針はアプリケーション側にも、 Google のサーバ側にもメリットがあります。 アプリケーション的にも扱う情報が減って取り回しやすく、またオブジェクトのサイズも削減できますし、 Google 的にもサーバ側での処理負荷が減るというメリットがあります。 Google のサーバに無駄な負荷を掛けても何もいいことはないので、サーバに優しいクエリを書くことを心がけたいですね。

「必要なフィールドが分からないからとりあえず雑に叩きたいんだけど」という場合もあるかと思いますが、 後述する Query Builder を上手く活用して乗り越えましょう。

Query Builder が便利

慣れてくるとこのクエリを手で組み立てることもできるかと思いますが

  • 「どのリソースにどういうフィールドを指定して良いのか」
  • 「そもそもどんなリソースに対してリクエストができるのか」

など、なかなか自力で有効なクエリを組み立てるのは骨が折れます。そこで Interactive Query Builder の出番です。

f:id:hano_tea:20190514190533p:plain
Interactive Query Builder 使用例

Web インターフェイス上で GAQL を組み立てることができる便利ツールです。取得したいリソースを指定すると、 そのリソースに対して指定できるフィールドが自動的に絞り込まれ、セレクトボックスからそれを追加できて非常に有用。 絞り込み条件や並び替えなども指定できます。

Google Ads API で何らかの情報を取得したくなったら、とりあえず Query Builder を使っておくのが良いと思います。便利。

(クエリをコピーしたときにタブが混入してしまうという欠点がありますが、手動でいい感じに直しましょう)

続いて「原子性」を持ったリソースの作成を紹介します。めっちゃ有用なAPIなのですが、 公式ドキュメントにもサンプルコードにもどこにも詳しい使い方が載っていないという謎の存在。

先日開催された Google Ads API Workshop Tokyo に参加した際に知ったのですが、 そこでも詳細までは解説されず、「こういうのがある」ぐらいの解説に留まる感じでした。

ずっと気になっていてチマチマ試していたのですが、先日ようやく実行に成功したので紹介させていただきます。

原子性?

なんとなく聞いたことあるけど、そもそも「原子性」って何のこと?という方もいらっしゃるかもしれませんので軽く。 e-Words の項目ページはこちら

原子性とは、ある物事が、それ以上細かい単位や要素に分割されない、またはできない性質のこと。

トノコト。

Google のセミナー中に使われていた言葉を借りると「すべて成功するかすべて失敗する」という特性です。 データベースでのトランザクション処理をイメージすると分かりやすいかもしれません。

たとえば、「広告アカウント、キャンペーン、広告グループの作成」を一つのまとまりとして作成したい、ということがあると思います。 これらのリソースを作成する際、「広告アカウント」と「キャンペーン」の作成は成功したが、「広告グループ」の作成に失敗してしまった場合を考えてみます。

各リソースを別々に作成してしまうと、「広告グループ」の作成で失敗したとしても「広告アカウント」と「キャンペーン」は既に作成済みの状態になってしまいます。 トランザクション処理を自前で書いて、どこかで失敗したら削除処理を行う…も手かもしれませんが、非常に複雑な処理を書かなければいけなくなりそうです。

こんなときに「原子性」を持ったリソースの作成が便利です。 Google Ads API に用意されている機能を使うと、上記のように「必要なリソースの作成中に一部が失敗した」とき、 同時に作成しようとしたリソースが全て作成成功しない限りただ一つのリソースも作成しない、というな処理を行うことが出来ます。

このように、原子性を持ったリソースの作成を利用すると、 01 、すべて作るかすべて作らないかという、トランザクション処理のようなことを実現できます。

サンプルコード

百聞は一見にしかず、です。実際に「キャンペーン」と「キャンペーン用の予算」、2つのリソースを原子性を持たせて作成してみます。

require 'google/ads/google_ads'

# 対象の Google 広告アカウントの id
customer_id = '1234567890'

client = Google::Ads::GoogleAds::GoogleAdsClient.new

# キャンペーン予算オペレーションの準備
# CampaignBudget オブジェクトを作成し、作成に必要な情報を設定する。
cbudget = client.resource(:CampaignBudget)
cbudget.name = client.wrapper.string(sprintf('Interplanetary Budget %s',(Time.new.to_f * 1000).to_i))
cbudget.delivery_method = client.enum(:BudgetDeliveryMethod)::STANDARD
cbudget.amount_micros = client.wrapper.int64(1000 * 1_000_000)

# resouce_name は通常設定しなくてよい値だが、キャンペーン作成用のオペレーションでこの値が必要になる。
# リクエスト実行時点では resource_name が不明なので、 `-1` のような負の値を仮の ID として定義する。
cbudget.resource_name = client.wrapper.string("customers/#{customer_id}/campaignBudgets/-1")

# CampaignBudgetOperation を作る。
cbudget_operation = client.operation(:CampaignBudget)
cbudget_operation['create'] = cbudget

# CampaignBudgetOperation を GoogleAdsService.mutate 用に MutateOperation でラップする。
mutate_cbudget_operation = client.operation(:Mutate)
mutate_cbudget_operation.campaign_budget_operation = cbudget_operation


# キャンペーン作成オペレーションの準備
# Campaign オブジェクトを作成し、作成に必要な情報を設定する。
campaign = client.resource(:Campaign)
campaign.name = client.wrapper.string(sprintf('Interplanetary Cruise %s',(Time.new.to_f * 1000).to_i))
campaign.advertising_channel_type = client.enum(:AdvertisingChannelType)::SEARCH
campaign.status = client.enum(:CampaignStatus)::PAUSED
campaign.manual_cpc = client.resource(:ManualCpc)

campaign.network_settings = client.resource(:NetworkSettings)
campaign.network_settings.target_google_search = client.wrapper.bool(true)
campaign.network_settings.target_search_network = client.wrapper.bool(true)
campaign.network_settings.target_content_network = client.wrapper.bool(false)
campaign.network_settings.target_partner_search_network = client.wrapper.bool(false)

campaign.start_date = client.wrapper.string(DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d'))
campaign.end_date = client.wrapper.string(DateTime.parse((Date.today.next_year).to_s).strftime('%Y%m%d'))

# さきほど作成した CampaignBudget リソースを使う。
# 一つの「原子性を持ったリソース作成リクエスト」内では仮 ID で別のリソースを参照できる。
campaign.campaign_budget = cbudget.resource_name

# CampaignOperation を作る。
campaign_operation = client.operation(:Campaign)
campaign_operation['create'] = campaign

# CampaignOperation を GoogleAdsService.mutate 用に MutateOperation でラップする。
mutate_campaign_operation = client.operation(:Mutate)
mutate_campaign_operation.campaign_operation = campaign_operation

# GoogleAdsService.mutate を使ってリソースを作成する。
# 第二引数の配列に、 MutateOperation でラップされた CampaignBudgetOperation と CampaignOperation を入れる。
ga_service = client.service(:GoogleAds)
ga_service.mutate(customer_id, [mutate_cbudget_operation, mutate_campaign_operation])

ポイント

要所要所にコメントを書いていますが、特に引っかかった点を2つ紹介します。

リソースに仮の resource_name を設定して別の箇所から参照できるようにする

原子性を持ったリソース作成時のキーポイントです。

原子性を持ったリソース作成では、一回のリクエストで複数のリソースを作成するため、 (当たり前ですが)リクエスト実行時はどのオブジェクトも作られていません。

また、リクエスト実行中に作成されたリソースの情報を取得して、その値を使って動的に…みたいなことも恐らくできません。

しかし、上記の「キャンペーン作成にキャンペーン予算リソースが必要」といったような、 同時に作成している関連する別リソースの resource_id が必要なケースは多々あります。

そこで登場するのが「仮の resource_name」です。 resource_name が仮と言うよりは id が仮な感じがありますが、便宜上こうしています。

通常、 resource_idcustomers/1111122222/campaignBudgets/3333344444 のような形式の文字列です。 数値部分がパス上で直前にあるリソースの id になります(今回は適当な値です)。 上記の例であれば

  • 1111122222 という idCustomer

の下にある

  • 3333344444 という idcampaignBudget

であることを示しています。

上記の例のように、各リソースの id は基本的に正の整数ですが、 仮の resource_name を指定する際にはこの id任意の負の整数 にします。

実際、キャンペーンとキャンペーン予算の作成を同時に行ったサンプルコード中では、 キャンペーン予算の resource_name を、

cbudget.resource_name = client.wrapper.string("customers/#{customer_id}/campaignBudgets/-1")

のように指定しています。

こうして仮の resource_name (仮の id) を付与してあげることで、 参照したい側でも同じ resource_name を使うことができるようになります。 サンプルコード中では campaign.campaign_budget = cbudget.resource_name と記述している部分です。 ここでは cbudget オブジェクト内の値を使いまわしていますが、中身は customers/#{customer_id}/campaignBudgets/-1 という文字列です。

これで、相互に関連するリソースをまとめて複数作成する際、各リソースをお互いに参照できるようになります。

余談ですが、私は最初「仮の id」というところに惑わされて resource_id ではなく id に負の整数値をセットして試していましたが、上手く動きませんでした。「仮の id」をセットするって言われたら素直に id に値を入れたくなりませんか…?

各リソース毎の Operation オブジェクトは MutateOperation でラップする必要がある

一番ハマったところかもしれない。

私は最初、 ga_service.mutate(customer_id, [array, of, operations]) というメソッドの第二引数に、普通に CampaignOperationCampaignBudgetOperation の配列を渡せば良さそう…と思っていて、しばらくそれを試していました…が、これがうまくいかない。

エラーを見ても何で怒られているのかが全然ピンと来ない。あんまりわからないのでバグかと思って Issue を立ててしまったぐらい。

コメントに書いてありますが、各リソース用の Operation をそのまま使うことはできず、一回 MutateOperation という(おそらく) GoogleAdsService.mutate 用の Operation で一回ラップしないといけないようでした。

...
# CampaignOperation を作る。
campaign_operation = client.operation(:Campaign)
campaign_operation['create'] = campaign

# CampaignOperation を GoogleAdsService.mutate 用に MutateOperation でラップする。
mutate_campaign_operation = client.operation(:Mutate)
mutate_campaign_operation.campaign_operation = campaign_operation
...

この辺は Protocol Buffers の型の都合でしょうか…?

そもそも自分は MutateOperation という Operation が存在していると認識できておらず、各リソース用の Operation の集合が MutateOperation と総称されている、という勘違いをしていたので上手くリクエストできなかった、という感じでした。難しいですね。

この辺、分かってからドキュメントを読むと「そういう意味だったのか」という気持ちになります。 今なら以下のような構成になってることがわかりますが、初見だと全然分かりませんでした…

  • GoogleAdsServiceMutateGoogleAdsRequest を引数にとる。
  • MutateGoogleAdsRequest 内の mutate_operations[] で実行したい Operation を指定する。型は MutateOperation
  • MutateOperation は様々なリソース用の Operation のフィールドを持ったもので、その中の一つのみに実際の Operation をセットする。

その他 Google Ads API Client for Ruby を扱う上で気をつけたいポイント

以上、「Google Ads API で情報を取得する基本的な方法」と「原子性を持ったリソース作成」の紹介でした。 ここでは上記であんまり触れなかった、特に API Client for Ruby を使う際にちょっと気をつけたいポイントについて触れておきたいと思います。

文字列や数値は基本的に client.wrapper を使ってラップする

Protocol Buffers の世界でやり取りをしないといけないので、 Ruby の文字列や数値はそのままでは基本的に受け付けてくれません。 client.wrapper 以下にそのあたりをラップしてくれる機能があるので、基本的にはそれらを通してあげる必要があります。

client = Google::Ads::GoogleAds::GoogleAdsClient.new

# 文字列
sample_str = client.wrapper.string('test string')

# 数値
sample_int = client.wrapper.int64(1000)

他には bool, float, double, bytes なども同様にラップできます。

Google が用意してくれているユーティリティ関数群を利用する

この記事を書いている最中に「あ、こんなユーティリティもあったんだ」と気付いたぐらいなのですが、いろいろなユーティリティを用意してくれています。

例えば client.resource(:ResourceName)ResourceName は例。任意のリソース名が入ります)。 これは Google::Ads::GoogleAds::Vx::Resources::ResourceName.new のショートハンドです。 愚直に全部書くと長いので、積極的にユーティリティを使っていきたいところ。バージョンの変更とかもしなくて済みそうです。(良いかどうかは場合によりそうですが)

他にも、サンプルコードで使っていない…というか今知ったのですが、 resource_name 用のユーティリティもあったりします。 client.path.campaign(customer_id, campaign_id) のように書くと、 customers/#{customer_id}/campaigns/#{campaign_id} になります。便利。

先に出てきた client.wrapper もユーティリティの一つです。これを使わないと以下のように毎回書かないといけないので面倒くさいです。

v = 'hogehoge'

# 文字列変換 without wrapper
Google::Protobuf::StringValue.new(value: v)

# with wrapper
client.wrapper.string(v)

おわりに

以上、「Google Ads API 徹底入門」でした。

まだあまり Google Adwords API の利用実績が多くないのか、 公開されている情報が公式リファレンス以外だとかなり少ないのが現状だと思います。

サードパーティの情報が少ないからこそ、少しでも情報を共有していくのが大事だと思い、この記事を執筆した次第です。

移行の際、あるいは新規に Google 広告関係の開発を行う際の助けになれば幸いです。