【Ruby】Facebook広告のデータをAPIで取得する

# 環境

Ruby 2.6.6
Rails 6.0.4.7
Gem facebookbusiness(Metaが出している公式のSDK)
(※ ググるとgem ‘facebook_ads’などが出てくるが、そちらではないことに注意)
API version v14.0(2022年8月最新)

# やりたいこと
1. Meta for DeveloperのAppにアクセス許可を渡しているすべての広告アカウントを取得する
2. それぞれの広告アカウントの、昨日のパフォーマンス(インプレッション、クリック、コストなど)を取得する

# ビジネスのちょっとした背景

ウチの会社はweb広告に力を入れていて、すべての事業のfacebook 広告アカウントを足すと使っていないものも含めて80アカウントくらいある…。(広告代理店かってくらい)

以前は各アカウントの担当者が毎日毎日手作業でgoogle spreadsheetに情報を入力して、ピボットやシートの連携で集計して、目視で確認して…とだいぶ作業が煩雑になっていた。それを、facebookのapiを叩いてspreadsheetの入力まで自動化してしまおうというのが今回の背景。

この記事ではfacebookの情報を呼び出すところまでを解説する。

# 前提

###  1. Meta for DeveloperのAppを作っていること

access tokenやapp secretをまだ生成していない場合は作っておいてください。この辺は日本語でもたくさん記事が転がっているので困らないはず。

### 2. Facebookの広告管理の構造について理解していること

ユーザーがいて、広告アカウントがあって、その中にキャンペーンがあって、広告セットがあって…などなど、基本的な構造や、imp、click、costなどPPC広告の基礎用語なんかは理解しておくとよい。

このあたりを前提知識として、facebookのAPIの概念から実際の叩き方をお伝えしてく。

# そもそもFacebookのAPIってどんなの?

### ・キーワードは「ノード」と「エッジ」

詳しくは[公式ドキュメント](https://developers.facebook.com/docs/graph-api?locale=ja_JP)に任せるが、ざっくり云えば、、、

:::note info
**ノード：「ユーザー」とか「広告アカウント」といったオブジェクト(のインターフェース)**

Javascriptを知っている人であれば、DOMのノードと同じような概念として馴染めると思う。
:::

:::note info
**エッジ：オブジェクトとオブジェクトを紐付けるもの**

DBのリレーションとかJOINのような概念。例えば、広告アカウントにある(複数の)キャンペーンを呼び出したい場合、`AdAccount.campaigns` のように書いて呼び出せるが、このドット以下の部分がエッジ。このエッジによってオブジェクトに紐づくオブジェクトを連鎖的に呼び出すことが可能になっている。

:::

### ・Rubyでは、「facebookbusiness」というGemを使って叩くことができる

上記のノードやエッジといったものを、httpリクエスト⇔レスポンスという形ではなく、オブジェクトとメソッドのように扱えるようにしたものがこのGem。

ちなみに、**facebook側の自動Gemアップデートによって常に最新のAPIバージョンが叩かれるようになっている**ので、基本的にメンテナンスはaccess_tokenの張替えだけでいい。

# 下準備

まずはGemを用意してインストールしておく。

```ruby:Gemfile
gem 'facebookbusiness'
```

初期設定

```ruby:facebook_api.rb
require 'facebookbusiness'

class FacebookAPI
	def initialize
		# facebook APIの初期設定
    FacebookAds.configure do |config|
      config.access_token = '<発行したアクセストークン>'
      config.app_secret = '<発行したアプリ・シークレット>'
    end
	end
end
```

※トークンなどは適宜クレデンシャルに突っ込んでおくこと。ちなみに、アクセストークンには「短期」(1〜2時間で切れる)と「長期」(2ヶ月)のものが存在する。以前は長期アクセストークンから無期限のトークンを発行できたが、現在はできない模様。

# 1：自分のアカウント情報を取得する

```ruby
user = FacebooAd::User.get('me')
#<FacebookAds::User {:id=>"me"}>
```

ここでのポイントは2つ


:::note info 
**1)「me」というのは特別なノードとして扱われている**

普通.get(id)でUserの情報にアクセスするが、「me」というノードを指定することで、リクエスト時にくっついてくるアクセストークンやアプリシークレットを見てAPI側が勝手に引っ張ってきてくれる。
:::

:::note info
**2)この段階ではまだAPIを叩いていない**

ただgetと書いただけではAPIは叩かれず、具体的な情報を取ってくる必要があるメッセージを送ったタイミングでAPIが叩かれることになる。
:::

```ruby
user = FacebooAd::User.get('me')
user.name
# "寺尾 たくみ"
```

この`name` のように、オブジェクトが保有してる情報をフィールドと呼ぶ。これは公式ドキュメントでも確認できるし、gemの中身を見れば、、、、

```ruby:user.rb
module FacebookAds
	class User < AdObject
		field :about, 'string'
    field :age_range, 'AgeRange'
    field :birthday, 'string'
    field :cover, 'UserCoverPhoto'
    field :currency, 'Currency'
    field :education, { list: 'object' }
    field :email, 'string'
    field :favorite_athletes, { list: 'Experience' }
    field :favorite_teams, { list: 'Experience' }
    field :first_name, 'string'
    field :gender, 'string'
    field :hometown, 'Page'
    field :id, 'string'
		...
		...
		...
	end
end
```

こんなふうにズラーッと並んでいるので、この一覧からほしい情報を検索してもいい。恐れずに[gemをgit cloneしよう](https://github.com/facebook/facebook-ruby-business-sdk)。

また、ほとんどのオブジェクトはAdObjectというクラスを継承しているので、いろんな便利メソッドが使える。

```ruby
user = FacebookAds::User.get('me')
user.load! # 今すぐAPIを叩いて自身のインスタンスを書き換える
user
#<FacebookAds::User {:id=>"<自分のアカウントID>"}>

user = FacebookAds::User.get('me',['name', 'id']) # argにほしいフィールドを配列でもたせる
user.load!
user.inspect
#<FacebookAds::User {:id=>"<自分のアカウントID>", :name=>"寺尾 たくみ"}>

user.to_hash
#{:id=>"<自分のアカウントID>", :name=>"寺尾 たくみ"}
```

恐れずに[gemをgit cloneしよう](https://github.com/facebook/facebook-ruby-business-sdk)。

# 2：自分のアカウントに紐づく広告アカウントを取得する

今回欲しいのは、Userに紐づくすべての広告アカウント。ここでgemのエッジを確認する。

```ruby:user.rb
class User < AdObject
	### 略 ###
	has_edge :accounts do |edge|
    edge.get 'Page' do |api|
      api.has_param :is_place, 'bool'
      api.has_param :is_promotable, 'bool'
    end
	...
	end

	### 略 ###
	
	has_edge :adaccounts do |edge|
    edge.get 'AdAccount'
  end
end
```

アカウントっぽいものは2つあるが、上のものはおそらくfacebookページ。広告アカウントは下の方の`adaccounts`になっていることがわかった。

```ruby
user = FacebookAds::User.get('me')
user.adaccounts
#<#<Class:0x0000aaaaf8eaea18>:0x0000aaaafa39f210
 @name=:adaccounts,
 @node=#<FacebookAds::User {:id=>"me"}>,
 @options={},
 @should_delegate=false>
```

この段階では、ノード(=オブジェクト)がUserで、エッジの名前である@nameがadaccountsであるプレーンなオブジェクトになっている。

```ruby
user.adaccounts.all # allですべて取り出す

#[#<FacebookAds::AdAccount {:account_id=>"xxx", :id=>"yyy"}>,
 #<FacebookAds::AdAccount {:account_id=>"xxx", :id=>"yyy"}>,
 #<FacebookAds::AdAccount {:account_id=>"xxx", :id=>"yyy"}>,
 #<FacebookAds::AdAccount {:account_id=>"xxx", :id=>"yyy"}>]
```

allの中身を確認するにはさすがにAPIを叩かないといけないので、ppを取った結果は上記のようにAdAccountというオブジェクトとそのフィールド(id)を取得している。

# 3：広告アカウントのデータを取得する

広告アカウントのオブジェクトが取得できたところで、次はそのアカウントがどんなパフォーマンスだったのかデータを呼び出したい。(インプレッション、クリック、コストなど)

このデータはAdInsightsという別のオブジェクトのフィールドになっているので、広告アカウントからエッジを使ってAdInsightsに接続する。

```ruby
user.adaccounts.each do |account|
	fields = %w[account_id account_name impressions inline_link_clicks spend]
	account.insights(fields: fields).each do |insight|
		insight.inspect
	end
end
#<FacebookAds::AdsInsights {:account_id=>"xx", :account_name=>"なまえ", :impressions=>"1000", :inline_link_clicks=>"100", :spend=>"10000", :date_start=>"2022-07-06", :date_stop=>"2022-08}>
```

ポイントは、エッジにもフィールドの指定ができること。これでデータが無事呼べたので、、、

```ruby
insight.impressions
# 1000

insight.spend
# 10000
```

など、好きに使うといい。ただし、取ってこれてない値を参照しようとするとAdObjectが持っている#load!でコケるので注意。特に数値系は値が0だと取ってこれないので、安牌を取るなら…

```ruby
insight[:impressions]
# 1000

insight.[:spend]
# 10000

insight[:conversions]
# nil
```

このようにシンボルで呼び出してやるといい。

# 4：パラメータをつけて条件を絞る

先程呼び出したデータは、デフォルトで1ヶ月間?の集計データを呼んでいる。が、こちらとしては昨日の集計だけ呼んで、毎日バッチを回して管理したい…。そこで、パラメータをつけて絞り込みをかける。

AdInsightsオブジェクトはAPIの中でもinsights APIが担当しているので、そのドキュメントのリファレンスを参照しなければならないことに注意。

・Graph API
・Insights API
・Marketing API

と3つのAPIに分かれているので、自分がほしいオブジェクトのAPIを探してリファレンスを見るべし。

```ruby
fields = %w[account_id account_name impressions inline_link_clicks spend]
target_date = '2022-08-05'
time_range = { 'since': target_date, 'until': target_date }

account.insights({ fields: fields, time_range: time_range}).each do |insights|
	insights.inspect
end
```

今回はtime_rangeで昨日だけのデータを呼び出した。フィールドとセットで設定する場合は、中括弧でこれらを挿入する。

# おわりに

faceboookのAPI、特に広告アカウントの情報を取ってくるものについては国内の情報がほとんどない...。この記事がfacebookのAPIに挑む方の手助けになれば幸いです。