Instagram Graph APIでユーザー、メディア、インサイトなどの情報を取得してみる

![Untitled Diagram.drawio(2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/2011589b-b28b-a454-85b2-ec54b4e3dfd9.png)

## 背景

最近、Instagram市場をターゲットとしたプロジェクトで業務を行っており、分析のために色々と情報収集をしているのですが、無数に存在するインフルエンサーを手動で調査するのはさすがにしんどいので何か方法は無いかと探していたところ、どうやら「[Instagram Graph API](https://developers.facebook.com/docs/instagram-api)」なるものを使えば比較的簡単に入手できるとの事。

今回はユーザーのフォロー数、フォロワー数、メディア（投稿）数といった基本的な情報をはじめ、リーチ（メディアを閲覧したユニークユーザーの合計数）やインプレッション（メディアが閲覧された合計回数）といった少し踏み込んだ情報までをAPI経由で取得してみたいと思います。

## 使用する技術・サービス

- Python3
- [Instagram Graph API](https://developers.facebook.com/docs/instagram-api)

※記事内ではDockerを使って環境構築を行っていますが、この辺は各々の判断に任せます。最終的にPython3が実行できる環境であればOK。

## 事前準備

まず大前提として、Instagram Graph APIを使用するためには以下の2つが必要となります。

- InstagramのビジネスアカウントID
- アクセストークン

それぞれの取得方法については、次の記事を参考にしてください。

参照記事: [Instagram Graph APIとGAS（Google Apps Script）を使ってインスタグラムのフォロワー数を自動的に日々記録する](https://qiita.com/joh_luck/items/af6f02c43e2f4497e15e)

これよりもわかりやすく丁寧に解説する自信が無いので、ここでは全て委ねさせていただきます（素晴らしい記事をありがとうございました。）

## 実装

準備ができたらコードを書いていきましょう。

### 作業ディレクトリ & 各種ファイルを作成

```
$ mkdir instagram-graph-api-client && cd instagram-graph-api-client
$ touch Dockerfile docker-compose.yml instagram_graph_api.py main.py　.env requirements.txt
```

### コード

```dockerfile:./Dockerfile
FROM python:3
USER root

RUN apt-get update
RUN apt-get -y install locales && \
    localedef -f UTF-8 -i ja_JP ja_JP.UTF-8

ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm

ADD . /app
WORKDIR /app

RUN apt-get install -y vim less
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools
RUN pip install -r requirements.txt
```

```yml:./docker-compose.yml
version: "3"
services:
  python3:
    restart: always
    build: .
    container_name: python3
    working_dir: /app
    tty: true
    volumes:
      - .:/app
```

```py:./instagram_graph_api.py
from time import sleep
import requests
import textwrap

BASE_URL = "https://graph.facebook.com/v11.0" # APIのバージョンを指定

class InstagramGraphApi:
    def __init__(self):
        self.base_url = BASE_URL
    
    # ユーザーを取得
    def fetch_user(self, username, business_account_id, access_token, data_fields):
        url = f"{self.base_url}/{business_account_id}"
        fields = f"business_discovery.username({username}){{{data_fields}}}"
        params = { "fields": fields, "access_token": access_token}

        return requests.get(url, params = params).json()["business_discovery"]

    # メディアを取得
    def fetch_media(self, username, business_account_id, access_token, data_fields):
        url = f"{self.base_url}/{business_account_id}"
        fields = f"business_discovery.username({username}){{media{{{data_fields}}}}}"
        params = { "fields": fields, "access_token": access_token}
        
        res = requests.get(url, params = params).json()["business_discovery"]
        
        media = []
        
        for i in range(len(res["media"]["data"])):
            media.append(res["media"]["data"][i])

        # Instagram Graph APIの仕様上、一度のリクエストで取得できるのは25件までなので、それ以上取得したい場合は複数回リクエストを送る        
        if "after" in res["media"]["paging"]["cursors"].keys():
            after = res["media"]["paging"]["cursors"]["after"]
            
            while after is not None:
                url = f"{self.base_url}/{business_account_id}"
                fields = f"business_discovery.username({username}){{media.after({after}){{{data_fields}}}}}"
                params = { "fields": fields, "access_token": access_token}
                
                res = requests.get(url, params = params).json()["business_discovery"]

                for i in range(len(res["media"]["data"])):
                    media.append(res["media"]["data"][i])
                
                if "after" in res["media"]["paging"]["cursors"].keys():
                    after = res["media"]["paging"]["cursors"]["after"]
                else:
                    after = None
                
                sleep(1) # API制限にかからないよう適度に時間を空ける

        return media
    
    # インサイト（ユーザー）を取得
    def fetch_user_insight(self, business_account_id, access_token, metric, period):
        url = f"{self.base_url}/{business_account_id}/insights"
        params = { "metric": metric, "period": period, "access_token": access_token}

        return requests.get(url, params = params).json()["data"]
    
    # インサイト（メディア）を取得
    def fetch_media_insight(self, media_id, access_token, metric):
        url = f"{self.base_url}/{media_id}/insights"
        params = { "metric": metric, "access_token": access_token}
        
        return requests.get(url, params = params).json()["data"]
```

引数の「fields」や「metric」の部分に欲しい値を渡しましょう。どんな値が取れるのかについては公式ドキュメントを参考にしてください。

- [https://developers.facebook.com/docs/instagram-api/reference/ig-user/business_discovery](https://developers.facebook.com/docs/instagram-api/reference/ig-user/business_discovery)
- [https://developers.facebook.com/docs/instagram-api/reference/ig-user/insights](https://developers.facebook.com/docs/instagram-api/reference/ig-user/insights)
- [https://developers.facebook.com/docs/instagram-api/reference/ig-media/insights](https://developers.facebook.com/docs/instagram-api/reference/ig-media/insights)

```py:./main.py
import os
import json
from instagram_graph_api import InstagramGraphApi
from dotenv import load_dotenv
load_dotenv()

def run():
    igapi = InstagramGraphApi()
    
    username = "username"                                  # ユーザー名
    business_account_id = os.getenv("BUSINESS_ACCOUNT_ID") # ビジネスアカウントID
    access_token = os.getenv("ACCESS_TOKEN")               # アクセストークン

    # name: 表示名、username:ユーザー名、biography: プロフィール文、follows_count: フォロー数、followers_count: フォロワー数、media_count: メディア数
    user = igapi.fetch_user(
      username,
      business_account_id,
      access_token,
      "name,username,biography,follows_count,followers_count,media_count"
    )
    
    # impressions: ユーザーのメディアが閲覧された合計回数、reach: ユーザーのメディアを1つ以上閲覧したユニークユーザーの合計数、profile_views: ユーザーのプロフィールが閲覧された合計回数
    user_insight = igapi.fetch_user_insight(
      business_account_id,
      access_token,
      "impressions,reach,profile_views",
      "day"
    )
    
    user_info = {
        "user": user,
        "insight": user_insight
    }

    # timestamp: タイムスタンプ、caption: 本文、like_count: いいね数、comments_count: コメント数
    media = igapi.fetch_media(
      username,
      business_account_id,
      access_token,
      "timestamp,caption,like_count,comments_count,mediaproducttype,media_type"
    )
    
    media_info = []
    
    for media in media:
        # engagement: いいね数・コメント数・保存数の合計、reach: メディアを閲覧したユニークユーザーの合計数、impressions: メディアが閲覧された合計回数、saved: 保存数
        media_insight = igapi.fetch_media_insight(
          media["id"],
          access_token,
          "engagement,reach,impressions,saved"
        )

        media_info.append({
            "media": media,
            "insight": media_insight
        })

    print("【ユーザー情報】\n")
    print(json.dumps(user_info, ensure_ascii = False))

    print("\n------------------------\n")

    print("【メディア情報】\n")
    print(json.dumps(media_info, ensure_ascii = False))

if __name__ == "__main__":
    run()
```

```text:./.env
BUSINESS_ACCOUNT_ID=<InstagramのビジネスアカウントID>
ACCESS_TOKEN=<アクセストークン>
```

事前準備の際に取得した値をそれぞれセットしてください。

```text:./requirements.txt
requests
python-dotenv
```

## 動作確認

それでは、実際に動かしてみましょう。

```
$ docker-compose up -d
$ docker exec -it python3 /bin/bash
$ python main.py
```

次のような形式のレスポンスが返ってくれば成功です。

```json:ユーザー情報
{
    "user": {
        "name": "************",
        "username": "************",
        "biography": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
        "follows_count": 123,
        "followers_count": 123456,
        "media_count": 1234,
        "id": "123456789"
    },
    "insight": [
        {
            "name": "impressions",
            "period": "day",
            "values": [
                {
                    "value": 1234,
                    "end_time": "2021-09-03T07:00:00+0000"
                },
                {
                    "value": 1234,
                    "end_time": "2021-09-04T07:00:00+0000"
                }
            ],
            "title": "インプレッション",
            "description": "ビジネスアカウントのメディアオブジェクトの合計ビュー数です",
            "id": "123456789/insights/impressions/day"
        },
        {
            "name": "reach",
            "period": "day",
            "values": [
                {
                    "value": 1234,
                    "end_time": "2021-09-03T07:00:00+0000"
                },
                {
                    "value": 1234,
                    "end_time": "2021-09-04T07:00:00+0000"
                }
            ],
            "title": "リーチ",
            "description": "ビジネスアカウントのメディアオブジェクトの合計ユニークビュー数です",
            "id": "123456789/insights/reach/day"
        },
        {
            "name": "profile_views",
            "period": "day",
            "values": [
                {
                    "value": 1234,
                    "end_time": "2021-09-03T07:00:00+0000"
                },
                {
                    "value": 1234,
                    "end_time": "2021-09-04T07:00:00+0000"
                }
            ],
            "title": "プロフィールビュー",
            "description": "指定された期間にビジネスアカウントのプロフィールを見た合計ユーザー数です",
            "id": "123456789/insights/profile_views/day"
        }
    ]
}
```

```json:メディア情報
[
    {
        "media": {
            "timestamp": "2021-09-03T00:00:00+0000",
            "caption": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
            "comments_count": 123,
            "id": "123456789"
        },
        "insight": [
            {
                "name": "engagement",
                "period": "lifetime",
                "values": [
                    {
                        "value": 123
                    }
                ],
                "title": "エンゲージメント",
                "description": "メディアオブジェクトの「いいね！」とコメントの合計数です",
                "id": "123456789/insights/engagement/lifetime"
            },
            {
                "name": "reach",
                "period": "lifetime",
                "values": [
                    {
                        "value": 1234
                    }
                ],
                "title": "リーチ",
                "description": "メディアオブジェクトを見たユニークアカウントの合計数です",
                "id": "123456789/insights/reach/lifetime"
            },
            {
                "name": "impressions",
                "period": "lifetime",
                "values": [
                    {
                        "value": 1234
                    }
                ],
                "title": "インプレッション",
                "description": "メディアオブジェクトの合計ビュー数です",
                "id": "123456789/insights/impressions/lifetime"
            },
            {
                "name": "saved",
                "period": "lifetime",
                "values": [
                    {
                        "value": 123
                    }
                ],
                "title": "保存",
                "description": "メディアオブジェクトを保存したユニークアカウントの合計数です",
                "id": "123456789/insights/saved/lifetime"
            }
        ]
    }
]
```

## あとがき

以上、Instagram Graph APIでユーザー、メディア、インサイトなどの情報を取得してみました。

なお、一点注意すべき点としては、おそらくインサイト情報については自分のものしか取得できなそうです。試しに他人のものを取得しようとしたところ、permissonエラーで弾かれました。（当たり前と言えば当たり前か...）

今回は「とりあえず触ってみる」という趣旨だったので比較的シンプルな情報のみの取得となりましたが、実際に取れる情報はもっとたくさんあるので、公式ドキュメントなどを参考に色々試していただければと思います。

https://github.com/kazama1209/instagram-graph-api-client