こんにちは id:hano_tea です。
真・Google Ads API 徹底入門の第二弾、 Search 編です。
前回の真・Google Ads API 徹底入門第一弾、基礎編は以下からどうぞ。
今回は Google Ads API から情報を取得するための仕組み Search
について解説します。
今回も前回と同様、 Ruby クライアントライブラリを使用している前提での解説になります。ご了承ください。
目次
- 目次
- 情報の取得 Search と SearchStream
- 基本的なつかいかた
- Search で使われるクエリ GAQL
- 実際に Search を試してみる
- レポートデータを取得する
- レスポンスオブジェクトの取り扱い Search編
- 実際にレスポンスの中身を見てみる
- レスポンスオブジェクトの取り扱い SearchStream 編
- Get は使ってはいけない
- 次回予告
情報の取得 Search と SearchStream
情報の取得は GoogleAdsService
を使用して行います。
なお、ここでいう「情報」とは、
- Google Ads 上の各種オブジェクト自体の情報
- 各種広告成果レポートデータ
などのことを指します。オブジェクトの情報取得とレポートデータの取得を同じ仕組み上で行うことができます。
ちなみに、 API v2 までは Search
というメソッドのみでしたが、 v3 から SearchStream
という新たなメソッドが追加されました。
この Search
と SearchStream
の違いについては後ほど説明します。
今は2種類のメソッドがあるということだけ頭の隅に置いておいていただければ。
基本的なつかいかた
Search
は Google Ads API で情報の取得を行う際に使用する最も基本的なメソッドです。
以下のようなコードで Search を実行することができます。
customer_id = '1112223333' query = '...' result = client.service.google_ads.search(customer_id, query)
ここで、customer_id
は「情報を取得したい対象となる広告アカウントの ID」です。
また、query
は「取得したい情報を指定するためのクエリ」です。
client
は前回の記事で紹介した Client のインスタンスです。
この後のコードも、基本的には client
という変数にインスタンスが格納されている前提となります。
Search
においては、情報の取得に使う query
文字列が重要になります。こちらについては次の項目で改めて説明します。
ここから先の解説はしばらく(SearchStream
も基本的な使い方はほぼ共通しているので)Search
に絞って解説します。
Search で使われるクエリ GAQL
Search で使われるクエリは Google Ads Query Language, 略称 GAQL の仕様に則って記述します。
Adwords のときも類似の概念として Adwords Query Language, 略称 AWQL というものがありましたが、これとは別物です。
SQL ライクな構文で取得したい情報について記述することができ、慣れるとドキュメントなしでもサクッとクエリを書けたりします。
書き方は以下のドキュメントを参照してもらうのが良いと思います。
基本的には以下のような構成になります。
SELECT 取得したい情報、メトリクス、セグメント FROM 取得対象のオブジェクト名 WHERE 取得対象のフィルタ条件 ORDER BY 取得したい情報やメトリクス (ASC | DESC) LIMIT 上限取得件数
PARAMETERS というものも追加されていましたが、使うタイミングはそこまで多くはなさそうなので、 気になる方はこちらのドキュメントを参照ください。
この中で最低限必要なのは SELECT
, FROM
のみです。以下のようなクエリは有効なものになります。
SELECT campaign.name FROM campaign
上記のクエリは、特定の… search
メソッドに渡した customer_id
で指定した広告アカウント以下にある、全てのキャンペーンの名前を取得します。
最低限過ぎてわかりにくいのでもう少し内容を増やしてみます。
キャンペーン ID が '2223334444' なキャンペーンの、キャンペーン名と resource_name
を取得する場合、以下のようになります。
SELECT campaign.name, campaign.resource_name FROM campaign WHERE campaign.id = '2223334444'
GAQL を生成する便利ツール Interactive Google Ads Query Builder
GAQL は慣れればリファレンスを見ながら手で書くこともできますが、 FROM に何を指定できるのか、 FROM に指定したものに対して SELECT は何が使えるのか、 はたまた WHERE の条件はどう書くのか…などなど、なかなか何も見ずにシュッと書くのは難しいものです。
そんな悩みをある程度解決してくれる便利ツールを Google は用意してくれています。
それが Interactive Google Ads Query Builder
です。
これを使うと、取得したいオブジェクトと、指定したいオブジェクトに対してどんなアトリビュートを指定できるか、 どんな条件で絞り込みができるかなどを確認しながらクエリを組み立てることができます。
特に、最初に取得対象のオブジェクトの種類を選択すると、それ以降の選択肢には 「そのオブジェクトに対して指定できる項目のみ」しか現れなくなるのでとても便利です。
ただし、これで組み立てたクエリが100%有効なものになっているという保証は無いのでご注意ください。 (クエリの文法的には有効だけどリクエストとしては不正なこともある)
実際に Search を試してみる
ということで実際に Search をしてみましょう。 以下のようなコードを入力し、キャンペーンを取得してみます。
customer_id = '...' # キャンペーン情報を取得したい広告アカウントの ID query = 'SELECT campaign.name FROM campaign' result = client.service.google_ads.search(customer_id, query) => #<Google::Gax::PagedEnumerable:0x000055f617c349a8 @func=#<Proc:0x000055f617c43098@/.../vendor/bundle/ruby/2.6.0/gems/google-gax-1.8.1/lib/google/gax/api_callable.rb:353>, @page= #<Google::Gax::PagedEnumerable::Page:0x000055f61848d708 @resource_field="results", @response= <Google::Ads::GoogleAds::V3::Services::SearchGoogleAdsResponse: results: [<Google::Ads::GoogleAds::V3::Services::GoogleAdsRow: ...
なんかわーっと色々な情報がコンソール上に流れていれば OK です。内容についてはこの後説明します。
レポートデータを取得する
上記の例ではキャンペーンオブジェクトそのものの取得をしてみましたが、同じようにレポートデータの取得もできます。
以下のようなクエリを発行すると、あるアカウントの全期間の、キャンペーン毎の広告成果を取得することができます。
query = 'select metrics.clicks from campaign' customer_id = '1112223333' result = client.service.google_ads.search(customer_id, query)
広告成果レポート系の項目は基本的に metrics を指定することで取得できます。色々ある上に表記が独特なものもあるので注意しましょう。 一覧は以下のページで参照できます。
が、一覧で見てもあんまり意味はないので…レポートを取得したくなった際は、 そのレポート取得対象のオブジェクトを Reports ページで探しましょう。例えばキャンペーンなら以下のページになります。
この中にレポート系で指定できる metrics
や segments
がまとまっているので、ここから必要な指標などを探すのが良いと思います。
(このあたりの画面の見方は別の記事で解説予定です)
レスポンスオブジェクトの取り扱い Search編
※ SearchStream
はレスポンスのオブジェクトが Search
と異なるものになるので注意してください。別途解説します。
さて、上記のように Search を使うことでオブジェクトの情報やレポートデータが取得できる、ということは分かりました。
ところで、このレスポンス…上記までのサンプルコードで言うところの result
には何が入っているのでしょうか。見てみましょう。
> result => #<Google::Gax::PagedEnumerable:0x0000556c36755628 @func=#<Proc:0x0000556c36754908@/.../vendor/bundle/ruby/2.6.0/gems/google-gax-1.8.1/lib/google/gax/api_callable.rb:353>, @page= #<Google::Gax::PagedEnumerable::Page:0x0000556c367cdc90 @resource_field="results", @response= <Google::Ads::GoogleAds::V3::Services::SearchGoogleAdsResponse: results: [<Google::Ads::GoogleAds::V3::Services::GoogleAdsRow: ...
おそらく結構な量の謎のログがわっと表示されたのではないかと思います。上記の例は大量に出てきたログの一部だけ切り取りました。
Google::Gax::PagedEnumerable
クラスのオブジェクトが返ってきていることが分かります。次の節ではこのオブジェクトの扱い方について説明します。
Google::Gax::PagedEnumerable
search
の結果は Google::Gax::PagedEnumerable
クラスのインスタンスとして返ってきます。(リンクは PagedEnumerable
の実装へのリンクです)
名前の通り、 Enumerable
で、 Ruby の Enumerable
を継承しています。また、これも名前の通りですが page
の概念があります。
page
は search
の結果のまとまりの単位、です。
例えば検索結果の件数が 100,000 件あったとき、Google からのレスポンスは 10,000 件毎の塊に分割されて取り扱われたりしますが、
この塊が page
です。search
では、API へのリクエストはこの page
単位で行われます。
1 つの page
を何件の塊として扱うかはリクエスト時に指定することができます。上限は 10,000 件です。
あまり大きな page
にするとリクエスト毎に大量のデータをやりとりすることになるので、必要十分なサイズに設定するのをオススメします。
Enumerable
なので取得結果に対する繰り返し処理を行うためのメソッドが色々使えます。 find
, select
, sort
, map
, to_a
, each
, reduce
などなど。
よく使う Enumerable
なメソッドとしては、
- 単純に全ての結果の配列だけ欲しいなら
to_a
- 全ての結果になにか手を加えた結果の配列が欲しいなら
map
あたりが挙げられるかと思います。
ちなみに、 Enumerable なメソッドを用いると、 次ページが必要になった時に自動的に次ページ取得リクエストが行われます 。
なので、 to_a
は全結果を取得して配列化するメソッドとして機能します。
また、 find
などは結果が見つかった時点で処理を打ち切るので、一部のページの取得だけで処理が終わることもあります。
Google::Gax::PagedEnumerable
独自に定義されているメソッドもありますが、内部で使われているものがほとんどで、
1ページ分の内容やメタデータ、サマリーなどが欲しいときに response
を使うぐらいでしょうか。
この response
の中身は Google::Ads::GoogleAds::Vx::Services::SearchGoogleAdsResponse
というクラスのオブジェクトです。
こちらについては後ほど説明します。
GoogleAdsRow
上述の PagedEnumerable
に対して to_a
を実行した際に得られる配列の中身や、
map
を実行した際にブロック引数として渡されるオブジェクトは GoogleAdsRow
というクラスになっています。
この中には クエリで指定した取得対象の情報 が格納されています。
Search で使ったクエリ、特に segment
を指定しているかどうかにもよるのですが、
例えばキャンペーンの一覧を取得するようなクエリで Search をした場合、キャンペーン1つにつき GoogleAdsRow
が1つ、
対応する形で情報が取得できます。
Search した結果を実際にコード中で使う際はこの GoogleAdsRow
から必要なデータを適宜取り出して扱う必要があります。
GoogleAdsRow
は以下のページにリファレンスがあります。
Row
の中にはクエリによって取得された各種オブジェクトの情報などが格納されています。
フィールドが大量にありますが、クエリで指定した部分だけしか中身が入らず、指定していないものは nil
あるいは空配列で返ってきます。
実際のレスポンス例を以下に示します。 Campaign
の情報を取得したときの例です。
<Google::Ads::GoogleAds::V3::Services::GoogleAdsRow: customer: nil, campaign: <Google::Ads::GoogleAds::V3::Resources::Campaign: resource_name: "customers/.../campaigns/...", id: nil, name: <Google::Protobuf::StringValue: value: "テスト用キャンペーン">, status: :UNSPECIFIED, campaign_budget: nil, ad_serving_optimization_status: :UNSPECIFIED, advertising_channel_type: :UNSPECIFIED, advertising_channel_sub_type: :UNSPECIFIED, tracking_url_template: nil, url_custom_parameters: [], network_settings: nil, experiment_type: :UNSPECIFIED, start_date: nil, end_date: nil, serving_status: :UNSPECIFIED, bidding_strategy_type: :UNSPECIFIED, bidding_strategy: nil, manual_cpc: nil, manual_cpm: nil, target_cpa: nil, target_spend: nil, base_campaign: nil, target_roas: nil, maximize_conversions: nil, maximize_conversion_value: nil, hotel_setting: nil, dynamic_search_ads_setting: nil, percent_cpc: nil, shopping_setting: nil, manual_cpv: nil, final_url_suffix: nil, real_time_bidding_setting: nil, frequency_caps: [], target_cpm: nil, video_brand_safety_suitability: :UNSPECIFIED, targeting_setting: nil, vanity_pharma: nil, selective_optimization: nil, tracking_setting: nil, geo_target_type_setting: nil, target_impression_share: nil, commission: nil, app_campaign_setting: nil, payment_mode: :UNSPECIFIED, labels: []>, ad_group: nil, metrics: nil, ad_group_ad: nil, ad_group_criterion: nil, bidding_strategy: nil, campaign_budget: nil, campaign_criterion: nil, keyword_view: nil, recommendation: nil, geo_target_constant: nil, ad_group_bid_modifier: nil, campaign_bid_modifier: nil, shared_set: nil, shared_criterion: nil, campaign_shared_set: nil, topic_constant: nil, keyword_plan: nil, keyword_plan_campaign: nil, keyword_plan_negative_keyword: nil, keyword_plan_ad_group: nil, keyword_plan_keyword: nil, change_status: nil, user_list: nil, video: nil, gender_view: nil, billing_setup: nil, account_budget: nil, account_budget_proposal: nil, topic_view: nil, parental_status_view: nil, feed: nil, display_keyword_view: nil, age_range_view: nil, campaign_draft: nil, feed_item: nil, hotel_group_view: nil, label: nil, managed_placement_view: nil, product_group_view: nil, language_constant: nil, ad_group_audience_view: nil, feed_mapping: nil, user_interest: nil, remarketing_action: nil, customer_manager_link: nil, customer_client_link: nil, campaign_feed: nil, customer_feed: nil, carrier_constant: nil, ad_group_feed: nil, search_term_view: nil, campaign_audience_view: nil, customer_client: nil, hotel_performance_view: nil, campaign_experiment: nil, extension_feed_item: nil, operating_system_version_constant: nil, mobile_app_category_constant: nil, customer_negative_criterion: nil, ad_schedule_view: nil, media_file: nil, domain_category: nil, feed_placeholder_view: nil, mobile_device_constant: nil, segments: nil, conversion_action: nil, custom_interest: nil, asset: nil, dynamic_search_ads_search_term_view: nil, ad_group_simulation: nil, campaign_label: nil, product_bidding_category_constant: nil, ad_group_criterion_simulation: nil, campaign_criterion_simulation: nil, ad_group_extension_setting: nil, campaign_extension_setting: nil, customer_extension_setting: nil, ad_group_label: nil, feed_item_target: nil, shopping_performance_view: nil, detail_placement_view: nil, group_placement_view: nil, ad_group_ad_label: nil, ad_group_criterion_label: nil, click_view: nil, location_view: nil, customer_label: nil, geographic_view: nil, landing_page_view: nil, mutate_job: nil, expanded_landing_page_view: nil, paid_organic_search_term_view: nil, ad_parameter: nil, ad_group_ad_asset_view: nil, distance_view: nil, currency_constant: nil, user_location_view: nil>
上記のように殆どの中身が nil
になっていることが分かります。(コンソールに表示するとだいたい酷いことになります)
GoogleAdsRow
から必要な中身だけ取り出す
上記のように GoogleAdsRow
は一見するとちょっと身構えてしまう感じの見た目になっていますが、必要な情報を取り出すのは難しくありません。
例えば上記のキャンペーンのレスポンスからキャンペーン名を取り出すのは以下のようにすれば良いです。
# row という変数に GoogleAdsRow オブジェクトが入っている想定
name = row.campaign.name.value
注意が必要な点が一つあり、キャンペーン名が格納されている row.campaign.name
には直接文字列が格納されているわけではなく、
Google::Protobuf::StringValue
というクラスのオブジェクトが入っています。
実際の文字列情報はこのオブジェクトの value
を取り出す必要があります。ちょっとめんどくさい。
今回取得したのはキャンペーン名なので StringValue
というクラスのオブジェクトでしたが、
整数値の場合は IntValue
, 浮動小数点値の場合は FloatValue
など、別のクラスになります。
その場合でも value
で取り出すという点は共通しているので、同じように値を取り出すことが出来ます。
ちなみに、上記の実際のレスポンスを見ると分かるのですが、
resource_name
は StringValue
ではなく直に文字列が入っているので value
を呼ばなくてもよかったりします。
SearchGoogleAdsResponse
PagedEnumerable
のところで軽く触れましたが、 response
というメソッドを使うと
SearchGoogleAdsResponse
クラスのオブジェクトが取得できます。
正確に書くと前述の通り Google::Ads::GoogleAds::Vx::Services::SearchGoogleAdsResponse
です。
Vx
は API バージョンが入ります。V3
みたいな感じ。
この中には主に、
- 1ページ分の検索結果
results
- 検索結果総件数
total_results_count
- 検索結果の集計家結果(サマリー)
summary_row
などのデータが格納されています。他にもいくつか入っているデータがありますが、よく使うのは上の3つです。
特に下2つの total_results_count
, summary_row
は SearchGoogleAdsResponse
でしか確認できない情報になっています。
実際にレスポンスの中身を見てみる
では実際にレスポンスオブジェクトの中身を見てみましょう。
前回のコードの実行時に result
という変数にレスポンスを格納しておいたので、それを見てみます。
とりあえず検索結果を全て見てみるため、 to_a
してみます。
> result.to_a => [<Google::Ads::GoogleAds::V3::Services::GoogleAdsRow: customer: nil, campaign: <Google::Ads::GoogleAds::V3::Resources::Campaign: resource_name: "customers/7775714709/campaigns/893252614", id: nil, name: <Google::Protobuf::StringValue: value: "【Seed data】テスト用キャンペーン">, ...
配列が返ってきていることは分かりますが、余計な情報が多くて何が格納されているのか、何が取得できているのか非常に見難いと思います。もうちょっとどうにかしてほしいところですが… 今回の例ではキャンペーン名を取得しているので、そこだけ取り出してみます。
> result.map { |row| row.campaign.name.value } => ["テスト用キャンペーン", ..., ...]
取得結果からキャンペーン名だけ抜き出しました。こんな感じに扱うことができます。 ちなみに、今回使っているテスト用アカウントは削除済みキャンペーンなどが多く含まれているので、こんな感じの結果が返ってきます。
response
の中も見てみましょう。
> result.response.total_results_count => 0 > result.response.summary_row => nil
何も取れませんね。 API v3 から、これらの値は明示的に取得することを設定しないと取得できなくなっています。
また、今回のクエリだとそもそもレポートデータが無いので、サマリ行は特に意味のある値は恐らく返ってこないと思われます。
以下のように Search しなおすと total_results_count
を取得することが出来るようになります。
> result = client.service.google_ads.search(customer_id, query, return_total_results_count: true) => ... > result.response.total_results_count => 22
レスポンスオブジェクトの取り扱い SearchStream 編
ここまでは Search を使った情報取得について説明してきました。 最初の方で Search とは別に SearchStream というものの存在を紹介しましたが、覚えているでしょうか?
SearchStream も Search と同様、 GAQL で取得したい情報についてのクエリを記述し、 それを用いてリクエストを行うことで欲しい情報を取得します。
では何が違うのかというと、以下のような点で違いがあります。
- SearchStream は、検索結果件数によらずリクエスト回数は1回になる
- SearchStream は、検索結果全件を取得するため、検索結果を見ながら取得件数を絞る、ということはできない
- SearchStream は、総検索件数 'total_results_count' を取れない
- SearchStream は、 Enumerator オブジェクトを返す
違いについての公式ドキュメントでの解説はこちら。
上記ドキュメントにも
By eliminating the round-trip network time required to request each individual page of a Search response, depending on your application, SearchStream may offer improved performance over paging. This advantage becomes more apparent as reports grow much larger.
とある通り、 使うタイミングとしては「大量のデータを取得したいとき」に使うのが有効です。目安的にはページング処理が入ってしまう 10,000 件以上のときでしょうか。
全件取得することが分かっている時にも SearchStream
を使っておいてもいいかもしれません。
文字で色々書いてもイマイチピンとこないので、実際に叩いてみましょう。
> result = client.service.google_ads.search_stream(customer_id, query)
> result
=> #<Enumerator: ...>
Enumerator
が返ってきています。とりあえず何も考えず全件取ってみましょう。 to_a
してみます。
> arr = result.to_a => [<Google::Ads::GoogleAds::V3::Services::SearchGoogleAdsStreamResponse: ...
SearchGoogleAdsStreamResponse
オブジェクトが返ってきました。
ちなみに、ここでもう一回 to_a
しようとするとエラーになります。ストリームとして一回処理が完了して閉じてしまったので、再度処理を走らせることはできません。
> arr = result.to_a > result.to_a => GRPC::Core::CallError: Cannot run batch on closed call
さて、一回目の to_a
の結果に話を戻します。 arr
に結果を格納しておいたのでそれを見てみます。
> arr = result.to_a => [<Google::Ads::GoogleAds::V3::Services::SearchGoogleAdsStreamResponse: results: [<Google::Ads::GoogleAds::V3::Services::GoogleAdsRow: ...
to_a
しているので当たり前といえば当たり前ですが、配列として結果が得られています。中身は SearchGoogleAdsStreamResponse
のようです。
SearchGoogleAdsStreamResponse
についてのドキュメントは以下。
SearchGoogleAdsStreamResponse
の中には results
が居て、 GoogleAdsRow
の配列が格納されています。
件数が多いリクエスト(1万件以上)をすると results
配列内の要素数が増えます。ちょうど Search
で 10,000 件のページサイズを指定したときと同じ感じに分割されます。
ここでは試しに全ての検索結果の配列を取得したいときの処理を考えてみます。
前述の通り、 to_a
した際の配列には SearchGoogleAdsStreamResponse
が格納されていました。
これにはページ単位のレスポンスが格納されているので、その中身まで見て取り出してあげないといけません。ちょっと面倒。
result = client.service.google_ads.search_stream(customer_id, query) result.flat_map do |page| pages.results.map do |row| ... end end
flat_map
という便利なメソッドがあるのでこれを使います。ただの map
だと最後に flatten
しないといけないのを省略できます。
flat_map
の中のブロック引数には page
に相当する SearchGoogleAdsStreamResponse
が来ますが、これは results
というアトリビュート内に配列で GoogleAdsRow
を持っているだけです。
なので、 page.results
で取り出した配列内の各要素を map
で順次取り出してあげる…というような流れの処理になります。
コード的には若干ややこしくなる(クラスメソッドの類があんまり充実してない)のが難点ですが、 大量の検索結果を扱わなければならない時の処理時間は劇的に短くなるので、使い所を選べば強力な武器になりそうです。
例えば、 100,000 件ちょっとあるような検索語句レポート全件の取得を Search で行うと結果の取得だけで30秒以上かかってしまいますが、 SearchStream を使うと 10 秒かからずに全件取得が可能でした。強力さが分かるかと思います。
Get は使ってはいけない
見出しでも書いていますが、 Google Ads API に用意されている各種 Get メソッドは 使用しないで ください。 大事なことなので何回も言います。
ここまで search をずっと見てきましたが、 Google Ads API には各種オブジェクトを取得するための get
メソッドが用意されていたりします。
例えば、以下の CampaignService
に生えている GetCampaign
メソッドがそれです。
これを使うと、指定したオブジェクトの全てのアトリビュートを一気に取得することができます。
できるのですが、これらの get メソッド群は 使用しない ようにしてください。叩くとしたら開発環境で日に数回使うぐらいに留めることを厳守してください。
下記の公式ドキュメントにも書いてある通り、 get
系メソッドの使用は非推奨です。
また、下記の API のリミットに関するページにもある通り、 get
は1日でたったの 1,000 回しか叩けません。
get
を使わなくてはいけないケースは恐らくほとんど存在せず、 search
のみで情報取得は完結出来るはずです。
get
は開発時に手元で試すに留め、アプリケーションコード上では使わないことをお勧めします。
[PR] うっかり get
を使ってしまうのを防止する Rubocop Custom Cop のご案内
上述の通り、 get
の使用は基本的に避けるべき…なのですが、普段から Google Ads API に触れていないと(下手すると触れていても)うっかり使ってしまう可能性があります。
レビューなどで防げれば良いですが、人間は見逃し、ミスをする生き物なので過信は出来ません。
ということで、人間の力に頼らず機械にチェックしてもらうための仕組みを用意しました。
Rubocop を導入されているプロジェクトなら、上記の Custom Cop を導入するだけで get
系メソッドの使用を検知、警告を出してくれるようになります。ご活用ください。
次回予告
さて、今回は真・Google Ads API 徹底入門 その2 Search 編ということで、 前回の基礎編の内容を踏まえ、 Google Ads API で情報を取得する際の基本について解説しました。
次回は Google Ads API で Google 広告の操作…オブジェクトの作成や編集、削除をする方法について解説していきます。お楽しみに。