ユーザーのインスタ写真を取得する

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/dae5981a-ed18-aea9-0577-c623ca19f1aa.png" width="150">
認証してくれたユーザーの投稿を取得できます。
**Webアプリでもモバイルアプリでも取得可能です。**
**Instagram Basic Display API** をつかいます。
手順に沿っていけば、そんなにややこしくないです。

**【画像：インスタグラムから画像データを取得してランダムに並べかえてみた】**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/e33927c7-5b90-bd42-fe6c-a2d5de9e7578.png" width="150">**→Get with API→**<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/329fd10c-46bf-bbb0-544f-b5b62f5eb7f4.png" width="150">


必要なものは、
**・Facebookのアカウント**
**・APIを利用するアプリ**
**・認証に使える自分のWEBサイトのURL**
ですが、とりあえずデータを取得してみるだけなら、アプリは仮のもので適当な名前で登録しても大丈夫です。
認証に使うWEBサイトも、自分で使える適当なサイトならとりあえずなんでもいいです。

#FACEBOOKForDevelopersにアプリを登録する

###１、FACEBOOK For Developersにログインする
[FACEBOOK For Developers](https://developers.facebook.com/?locale=ja_JP)をひらきます。
FACEBOOKの一般ユーザーアカウントでログインできます。FACEBOOKのアカウントがない場合は登録します。


###２、アプリを登録する
「マイアプリ」からアプリを作成します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/3c062791-589a-cd42-738b-780f97570256.png" width="500">

アプリのタイプを選択します。
アプリ名を登録します。
アプリのダッシュボードが表示されたら、[設定] > [基本]に移動し、ページの下までスクロールして[プラットフォームを追加]をクリックします。
任意のプラットフォームを選択します。
iOSの場合はバンドルIDを登録します。

###３、基本表示APIを設定する
ダッシュボードから[製品]をクリックして[Instagram基本表示]製品を見つけ、[設定]をクリックしてアプリに追加します。
基本表示APIの必要事項を記入します。

**表示名:**
先ほど作成したアプリの名前を入力します。

**有効なOAuthリダイレクトURI**
使用するウェブサイトのURLを入力します。これは、通常はリダイレクトクエリ文字列パラメーターをキャプチャできる専用URIですが、とりあえずテストするには、ご自分のウェブサイトのURLで問題ありません。
例: https://socialsizzle.heroku.com/auth/
URLの入力後、変更内容を保存し、もう一度URLをチェックしてください。URLの構造によっては、末尾にスラッシュが付加されている場合があります。正式なURLをどこかにコピーしておいてください。後のステップで、認証コードやアクセストークンを得るために必要になります。

**コールバックURLの許可の取り消し**
ウェブサイトのURLを再度入力します。これは、最終的には許可の取り消し通知を処理できるURLに変更しますが、とりあえずは、先ほど指定したウェブサイトのURLを再利用できます。

**データ削除リクエストコールバックURL**
ウェブサイトのURLを再度入力します。許可の取り消しコールバックURLと同様に、最終的にはデータ削除リクエストを処理できるURLに変更しますが、今のところは自分のウェブサイトのURLを再利用できます。

**アプリレビュー**
アプリをライブモードに切り替えるまではこのセクションをスキップします。

#ユーザーにアプリを認証してもらう

###４、テストユーザーを追加する
[役割] > [役割]に移動し、下にある[Instagramテスター]セクションまでスクロールします。[Instagramテスターを追加]をクリックしてInstagramアカウントのユーザーネームを入力し、招待を送信します。

**アプリが開発モードである間は、上記のように手動でテストユーザーを登録する必要があります。**
**テスト登録なしで一般ユーザーに認証してもらうには、Facebookの審査を受けてアプリをライブモードにする必要があります。**

###５、Instagramからテスト招待を受け入れる

新しいウェブブラウザーを開き、www.instagram.comにアクセスして、先ほど招待したInstagramアカウントにサインインします。(プロフィールアイコン) > [プロフィールを編集] > [アプリとウェブサイト] > [テスターの招待]に移動し、招待を受け入れます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/7306a752-f24f-46b5-624a-2a1bde3cad61.png" width="500">

###６、テストユーザー認証をして、認証コードを取得する
WEBブラウザから認証します。
以下の認証ウィンドウURLを作成します。
**{app-id}**
InstagramアプリのID ([アプリダッシュボード] > [製品] > [Instagram] > [基本表示] > [InstagramアプリID]フィールドに表示)に置き換えます。
**{redirect-uri}**
ステップ2で指定したウェブサイトのURLに置き換えます。(「有効なOAuthリダイレクトURI」)。URLは正確に同じものでなければなりません。

```
https://api.instagram.com/oauth/authorize
  ?client_id={app-id}
  &redirect_uri={redirect-uri}
  &scope=user_profile,user_media
  &response_type=code
```

たとえば、次のようにします。

```
https://api.instagram.com/oauth/authorize
  ?client_id=684477648739411
  &redirect_uri=https://socialsizzle.herokuapp.com/auth/
  &scope=user_profile,user_media
  &response_type=code
```


新しいブラウザーウィンドウを開いて、認証ウィンドウURLを読み込みます。
認証ウィンドウが表示されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/c684cd9d-67e6-3bc2-4ec0-3c3e05c64470.png" width="300">


認証ウィンドウにサインインしてInstagramテストユーザーを認証し、[許可]をクリックして、アプリにプロフィールデータへのアクセスを許可します。
成功すると、前のステップで指定したリダイレクトURIにリダイレクトされ、URLに認証コードが付加されます。
たとえば、次のようになります。

```
https://socialsizzle.herokuapp.com/auth/?code=AQDp3TtBQQ...#_

//（ブラウザのURL欄のURLの末尾に認証コードがついているのでコピーします。AQDp3TtBQQ...の部分です）
```

認証コードは、URL末尾の　code=以下の
AQDp3TtBQQ...
の部分です。
"# _"がリダイレクトURIの末尾に追加されていますが、これはコードそのものの一部ではありません。
次のステップで使用できるように、このコード（# _を除く）をコピーします。
**認証コードは短期で、1時間だけ有効です。**

#認証トークンを取得する
###６、:トークンを取得する

cURLリクエストをサポートするコマンドラインツール（Macでテストする場合はターミナル）またはアプリを開きます。
トークンを取得するために、次のPOSTリクエストをAPIに送信します。**（本記事ではターミナルのコマンドを用います。）**



```shell
curl -X POST \
  https://api.instagram.com/oauth/access_token \
  -F client_id={app-id} \
  -F client_secret={app-secret} \
  -F grant_type=authorization_code \
  -F redirect_uri={redirect-uri} \
  -F code={code}
```

{app-id}、{app-secret}、{redirect-uri}、{code}は、それぞれInstagramアプリID、Instagram app secret、リダイレクトURI、前のステップでコピーした認証コードに置き換えてください。
たとえば、次のようにします。

```shell
curl -X POST \
  https://api.instagram.com/oauth/access_token \
  -F client_id=684477648739411 \
  -F client_secret=eb8c7... \
  -F grant_type=authorization_code \
  -F redirect_uri=https://socialsizzle.herokuapp.com/auth/ \
  -F code=AQDp3TtBQQ...
```

注意点：
・末尾のスラッシュ(Instagramアプリを構成したときにアプリダッシュボードで追加された場合)を含めて、リダイレクトURIが前のステップで指定したものと完全に一致することを確認してください。
・client_secretはダッシュボードで●●●●●●の状態ではなく、きちんとアルファベットを表示してコピペする必要があります。**

成功すると、**1時間有効**な短期InstagramユーザーアクセストークンとInstagramテストユーザーのIDを含む、JSONエンコードされたオブジェクトがAPIから返されます。

>{
  "access_token": "IGQVJ...",
  "user_id": 17841405793187218
}

**このトークンとユーザーIDでユーザーのデータを取得するので、コピーしておきます。**

#認証トークンを使ってメディアを取得する

###７、メディアを取得する

メディアURLを取得するコマンドは以下です。

```shell
curl -X GET 'https://graph.instagram.com/me/media?fields=media_url&access_token={上記で取得したアクセストークンをペーストします}'
```

以下のようなJSON形式でメディアURLが返ってきます。
個々の投稿のurlとidのディクショナリの配列です。

>{"data":[{"media_url":"https:\/\/scontent.cdninstagram.com\/v\/t51.29350-15\/255437112_605858473995210_8575132173605956569_n.jpg?_nc_cat=100&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=M7WqnfYUom8AX9--x97&_nc_ht=scontent.cdninstagram.com&edm=ANo9K5cEAAAA&oh=cfb8526c1db4810e9b326175e592d19e&oe=618E3792","id":"17891471009375090"},{"media_url":https...


このURLはJSONエンコードされているので、そのままでは使うことができません。デコードする必要があります。
例えば、コマンドラインツールを使っているのであれば、jqを使ってデコードできます。

```shell
#jqのインストール
brew install jq
```

```shell
#コマンドの末尾に |jq をつける
curl -X GET 'https://graph.instagram.com/me/media?fields=media_url&access_token={上記で取得したアクセストークンをペーストします}'|jq
```

デコードされたデータが返ってきます。
>{
  "data": [
    {
      "media_url": "https://scontent.cdninstagram.com/v/t51.29350-15/255437112_605858473995210_8575132173605956569_n.jpg?_nc_cat=100&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=M7WqnfYUom8AX9--x97&_nc_ht=scontent.cdninstagram.com&edm=ANo9K5cEAAAA&oh=cfb8526c1db4810e9b326175e592d19e&oe=618E3792",
      "id": "17891471009375090"
    },
    {
      "media_url": "https:...
}

デコードされたmedia_urlからコンテンツを取得できます。
ブラウザにこのURLを貼り付けると画像が表示されます。

**【画像：インスタグラムから画像データを取得してランダムに並べてみた】**
![IMG_9002.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/e33927c7-5b90-bd42-fe6c-a2d5de9e7578.png)→APIで取得→![IMG_9001.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/329fd10c-46bf-bbb0-544f-b5b62f5eb7f4.png)


動画も取得できます。

#さまざまなデータを取得する

###メディアデータ

ユーザーのメディア投稿について、以下の情報を取得できます。
全ての投稿について一括で取得できます。

|フィールド名|説明|
|---|---|
|caption|メディアのキャプションテキスト。アルバム中のメディアについては返すことができません。|
|id|メディアのID。|
|media_type|メディアのタイプ。IMAGE、VIDEO、またはCAROUSEL_ALBUMのいずれか。|
|media_url|メディアのURL。|
|permalink|メディアの永続URL。メディアに著作権対象コンテンツが含まれている場合、または著作権違反のフラグが付いている場合は除外されます。|
|thumbnail_url|メディアのサムネイル画像のURL。VIDEOメディアでのみ使用可能。|
|timestamp|メディアの公開日付(ISO 8601形式)。|
|username|メディア所有者のユーザーネーム。|


データを取得するコマンドは以下のフォーマットです。

```shell
curl -X GET 'https://graph.instagram.com/me/media?fields={欲しい情報のフィールドをカンマ区切りで入力します}&access_token={アクセストークン}'
```

例えば、各投稿についてcaptionとtimestampを取得したい場合は以下のようにします。

```shell
curl -X GET 'https://graph.instagram.com/me/media?fields=caption,timestamp&access_token=IGQVJVQnJtN2xFdW80TjdtVnBtYXYxWkZAiMlRzek52bG5MU3FrVXY5R0hGNjF5M0haLUVmOUxUeHo4LXBoRW5ocl9xTHdWV2Y3VU40T3dpYUg1R3l5b0xnbUFYdTNSZA3lGZA0F6LVlHRVY1bXN6cGJUWHdaX29uVS1Va0ZAj'
```

このデータもjsonエンコードされているため、上記jqなどでデコードする必要があります。デコードすると以下のようなディクショナリが返ってきます。

>{
  "data": [
    {
      "caption": "ユニバ",
      "timestamp": "2021-11-09T23:44:46+0000",
      "id": "18112329574254917"
    },
    {
      "caption": "ディズニー",
      "timestamp": "2021-11-09T02:43:38+0000",
      "id": "17899173164360732"
    },
...
}

###複数画像アルバム投稿の全ての画像を取得する

上記のようなユーザー投稿の一括取得では、複数枚投稿の1枚目しか取得できません。
アルバムの全画像を取得するには、アルバムの子ノードを取得する必要があります。
このためには、投稿を一括取得した際に得られるメディアidを使用します。”id”として各投稿について返ってくるものです。
ユーザーidとは違うので注意してください。

```shell
curl -X GET 'https://graph.instagram.com/{media-id}/children?fields={fields}&access_token={access-token}'
```

例えば、アルバムの各画像のurlとメディアタイプを取得するには以下のようにします。

```shell
curl -X GET 'https://graph.instagram.com/18112329574254917/children?fields=media_url,media_type&access_token=IGQVJVQnJtN2xFdW80TjdtVnBtYXYxWkZAiMlRzek52bG5MU3FrVXY5R0hGNjF5M0haLUVmOUxUeHo4LXBoRW5ocl9xTHdWV2Y3VU40T3dpYUg1R3l5b0xnbUFYdTNSZA3lGZA0F6LVlHRVY1bXN6cGJUWHdaX29uVS1Va0ZAj'
```

>{
  "data": [
    {
      "media_url": "https://scontent.cdninstagram.com/v/t51.29350-15/255120157_1237525550081358_2923557396938869321_n.jpg?_nc_cat=111&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=6aJIC6deetoAX_0Rhyj&_nc_ht=scontent.cdninstagram.com&edm=ABbrh9MEAAAA&oh=611e60685ead210ac4e6ecee6e50bafe&oe=618F4346",
      "media_type": "IMAGE",
      "id": "17891715494364773"
    },
    {
      "media_url": "https:...


jsonエンコードされた子ノードの情報が返ってきます。
キャプションなどを子ノードについてクエリするとエラーが返ってきます。親ノード（アルバム投稿）に固有の情報だからです。

#長期トークンを取得する

上記アクセストークンは1時間しか使えません。
６０日間利用可能な長期アクセストークンを取得できます。

```shell
curl -i -X GET "https://graph.instagram.com/access_token?grant_type=ig_exchange_token&client_secret={instagram-app-secret}&access_token={短期トークン}"

#{instagram-app-secret}と{短期トークン}を固有のものにする
```

**アプリ・シークレットを含むリクエストであるため、このリクエストはクライアントではなくサーバーで行う必要があります。**

#長期トークンを更新する

```shell
curl -i -X GET "https://graph.instagram.com/refresh_access_token
  ?grant_type=ig_refresh_token
  &access_token={長期トークン}"
```

#iOS/Swiftでインスタ写真を取得する

URLSessionでリクエストすれば同様に取得できます。

```swift
let url = URL(string: "https://graph.instagram.com/me/media?fields=media_url&access_token=IGQVJXU1YzRWhUSVhjMjlPWkplWXFJcGloQ1hneW0yM2ZA5RmJxZA0JHbmlKT3R1UXZApdTFIS1FWOVFranl6YVl3a0JtNTdMLThTUERjZA3lZAUjBpeXoyR2c2eTkyMUhHS0pCeUNEQ1h3")!  //URLを生成
var request = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in  
    guard let data = data else { return }
    do {
           // 以下でJsonをFoundationで扱える形にして、Stringのディクショナリにする
           guard let object = try JSONSerialization.jsonObject(with: data, options: []) as? Dictionary<String, Any> else { print("Coudn't parse json object"); return }
           guard let media = object["data"] as? Array<Dictionary<String, String>> else { print("Coudn't parse json object"); return }
           print(media)
       } catch let error {
           print(error)
       }
}
task.resume()
```

🐣
****
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLやARKitを使ったアプリを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)