google driveからInstagramへ投稿する

備忘録として残しておこうと思います。
今回制作したものは、スクリーンショットをインスタグラムへ投稿するプログラムです。

また今回は「google drive api」と「Instagram graph api」を使用します。
各apiの詳しい説明はご自身で調べてください。
私が躓いたところを簡単に説明するだけにとどめますのでご了承ください。

## プログラムの流れ
1.pcでスクリーンショットを撮影,保存
↓
2.一旦Google Driveに保存する
↓
3.Google Drive上の写真をInstagramへ投稿

という流れになっています。

# １．スクリーンショットの撮影,保存
```python:screenshot.py
import os
import pyautogui

#保存先フォルダを選択する
folder_path="tes" 
#tes/tes2とするとtesフォルダの下に存在するtes2フォルダを選択できる。

#ファイル名を決定する。このとき.pngを書き忘れるとエラー出ます。
filename="lemon.png"

#スクショを撮ります。
screenshot = pyautogui.screenshot()
#ファイルパス作成。フォルダ名とファイル名を結合？する。
file_path = os.path.join(folder_path, filename)
#スクショの保存。指定したパスで保存する。
screenshot.save(file_path)
```

# ２．Google Driveに保存する
まずは、以下の動画を参考にGoogle Drive APIを導入してください。
https://www.youtube.com/watch?v=tamT_iGoZDQ&t=374s

動画内に出てきたコードを表示しておくのでお使いください。
```python:Gdrive.py
from googleapiclient.discovery import build
from google.oauth2 import service_account

SCOPES = ["https://www.googleapis.com/auth/drive"]
SERVICE_ACCOUNT_FILE = "service_account.json"
PARENT_FOLDER_ID = "your_PARENT_FOLDER_ID "
credentials = service_account.Credentials.from_service_account_file(
    SERVICE_ACCOUNT_FILE, scopes=SCOPES)

#動画内にはありませんがこれを追加したら成功しました。もし失敗したら使ってください。
#service = build('drive', 'v3', credentials=credentials) 

def authenticate():
    creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE,scopes=SCOPES)
    return creds

def upload_photo(file_path):
    creds = authenticate()
    service = build("drive","v3",credentials=creds)
    
    file_metadata = {
        "name" : "Hello", #drive上の名前を決める
        "parents" : [PARENT_FOLDER_ID]    
    } 

    file = service.files().create(
        body = file_metadata,
        media_body = file_path
    ).execute()


#pc内のファイルパスに置き換えてください。
upload_photo("xxxxxxx.png")
```

# ３．Google Drive上の写真をInstagramへ投稿
## 3-0.Instagram graph apiの性質
Instagram graph apiの仕様上、画像はインターネット上に存在する必要があります。
言い換えると、URLだけでInstagram側が画像にアクセスできるようにしなければいけません。
そのため、Google Drive上の写真にURLでアクセスできるように3-1で調整を行います。

## 3-1.Google Driveに保存した画像のIDを取得する
Google Driveに存在するファイルはそれぞれ固有のIDを持っています。
そのIDがわかると都合のよいURLを作成することができるので、
ここではIDを取得していきます。

以下のget_file_id関数を実行するとIDが取得できます。

**ですが**pythonの仕様上、この関数が動いている間のみIDがpythonに存在するらしいです。
そのため、無理やり次の関数に繋げました。
3-3のinstaapiという関数に繋がります。
(本当は変数に代入してわかりやすいコード書きたかったんですけどね...）
```python:checkID.py
from googleapiclient.discovery import build
from google.oauth2 import service_account
    
def get_file_id(file_name):
    results = service.files().list(
        q=f"name='{file_name}'",
        spaces='drive',
        fields="nextPageToken, files(id, name)").execute()
    items = results.get('files', [])
    if not items:
        print('No files found.')
    else:
        for item in items:
            image_url=f"https://drive.usercontent.google.com/u/0/uc?id={item['id']}&export=download"
            instaapi(image_url)#"instaapi"はあとで出てくる関数名です。

```
"instaapi"はあとで出てくる関数名です。

```
https://drive.google.com/file/d/[#固有のID]/view?usp=drive_link
https://drive.usercontent.google.com/u/0/uc?id=[#固有のID]&export=download
```
上の方でアクセスすると、google driveのフォルダに飛ばされます。
下の方でアクセスすると、すぐにダウンロードが始まります。
Instagram graph apiでは下の方をURLに指定します。

## 3-2.Instagram graph apiの導入

InstagramビジネスアカウントIDとアクセストークンを確認します。
ここは長く、複雑です。
記事の手順を遂次確認しながら進めてください。
私が参考にしたのは以下の２つです。
1つ目の記事だけでは、InstagramビジネスアカウントIDの確認ができないので２つ目の記事も参考にしてください。

https://tkstock.site/2023/03/08/%E3%80%902023%E5%B9%B4%E7%89%88%E3%80%91instagram-graph-api%E3%81%AE%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B%E6%96%B9/

https://quattroinc.jp/blog/52/#toc-6

## 3-3.Instagramへ投稿
導入が終了したら、以下のプログラムの
[BUSINESS_ACCOUNT_ID]　,　[PAGE_ACCESS_TOKEN]
を先ほど手に入れた自身のものに変えてください。

```python:InstaPub.py
import requests

BUSINESS_ACCOUNT_ID="your ID"#変更してください
PAGE_ACCESS_TOKEN="your token"#変更してください
#image_url="https://drive.usercontent.google.com/u/0/uc?id=[ID]&export=download"
#本当だったら、この変数を使いたかった。


#image_url はget_file_id関数にあります。
def instaapi(image_url):
    caption = "ここはキャプションです #python"
    # Upload the image to Facebook
    url = f"https://graph.facebook.com/{BUSINESS_ACCOUNT_ID}/media"
    params = {'access_token': PAGE_ACCESS_TOKEN, 'image_url':image_url, 'caption':caption}
    response = requests.post(url, params=params)
    if response.status_code != 200:
        raise Exception(f"Failed to upload image: {response.text}")
    media_id = response.json()['id']

    # Publish the photo to Instagram
    url = f"https://graph.facebook.com/{BUSINESS_ACCOUNT_ID}/media_publish"
    params = {'access_token': PAGE_ACCESS_TOKEN, 'creation_id': media_id}
    response = requests.post(url, params=params)
    if response.status_code != 200:
        raise Exception(f"Failed to publish photo: {response.text}")

```
これを実行するとあなたのinstagramアカウントに画像が投稿されます。

# まとめ
以下にすべてをまとめたコードを置いておきます。
sleep関数を使って、Instagramに投稿する前に１０秒間のインターバルを設定しています。
また、私の環境ではgoogle driveに保存する画像の名前に.pngを付けないとバグりました。
お気をつけてください。
```python:matome.py
import os
import pyautogui
import requests
from googleapiclient.discovery import build
from google.oauth2 import service_account
import time




#################screen shot##############################


#保存先フォルダを選択する
folder_path="tes" 
#tes/tes2とするとtesフォルダの下に存在するtes2フォルダを選択できる。

#ファイル名を決定する。このとき.pngを書き忘れるとエラー出ます。
filename="lemon.png"

#スクショを撮ります。
screenshot = pyautogui.screenshot()
#ファイルパス作成。フォルダ名とファイル名を結合？する。
file_path = os.path.join(folder_path, filename)
#スクショの保存。指定したパスで保存する。
screenshot.save(file_path)

print("1")
##################instagram#####################################################

BUSINESS_ACCOUNT_ID="your ID"#変更してください
PAGE_ACCESS_TOKEN="your token"#変更してください
#image_url="https://drive.usercontent.google.com/u/0/uc?id=[ID]&export=download"
#本当だったら、この変数を使いたかった。


#image_url はget_file_id関数にあります。
def instaapi(image_url):
    caption = "ここはキャプションです #python"
    # Upload the image to Facebook
    url = f"https://graph.facebook.com/{BUSINESS_ACCOUNT_ID}/media"
    params = {'access_token': PAGE_ACCESS_TOKEN, 'image_url':image_url, 'caption':caption}
    response = requests.post(url, params=params)
    if response.status_code != 200:
        raise Exception(f"Failed to upload image: {response.text}")
    media_id = response.json()['id']

    # Publish the photo to Instagram
    url = f"https://graph.facebook.com/{BUSINESS_ACCOUNT_ID}/media_publish"
    params = {'access_token': PAGE_ACCESS_TOKEN, 'creation_id': media_id}
    response = requests.post(url, params=params)
    if response.status_code != 200:
        raise Exception(f"Failed to publish photo: {response.text}")


print("2")
###############google driveへ保存する###################################

SCOPES = ["https://www.googleapis.com/auth/drive"]
SERVICE_ACCOUNT_FILE = "service_account.json"
PARENT_FOLDER_ID = "your_PARENT_FOLDER_ID"
credentials = service_account.Credentials.from_service_account_file(
    SERVICE_ACCOUNT_FILE, scopes=SCOPES)

#動画内にはありませんがこれを追加したら成功しました。もし失敗したら使ってください。
service = build('drive', 'v3', credentials=credentials) 

def authenticate():
    creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE,scopes=SCOPES)
    return creds

def upload_photo(file_path):
    creds = authenticate()
    service = build("drive","v3",credentials=creds)
    
    file_metadata = {
        "name" : "Hello.png", #drive上の名前を決める.pngを付けないとエラー吐かれる
        "parents" : [PARENT_FOLDER_ID]    
    } 

    file = service.files().create(
        body = file_metadata,
        media_body = file_path
    ).execute()

#google drive上のファイルのID取得
def get_file_id(file_name):
    results = service.files().list(
        q=f"name='{file_name}'",
        spaces='drive',
        fields="nextPageToken, files(id, name)").execute()
    items = results.get('files', [])
    if not items:
        print('No files found.')
    else:
        for item in items:
            image_url=f"https://drive.usercontent.google.com/u/0/uc?id={item['id']}&export=download"
            instaapi(image_url)
            #"instaapi"は関数名です。
            print("4")

#pc内のファイルパスに置き換えてください。
#関数の実行
upload_photo("tes\lemon.png")
print("3")

#念のために１０秒間待機します
time.sleep(10)
#.pngつけないとエラー吐かれます
file_name="Hello.png"
#googledrive上の名前を選ぶ、今回は上の変数に代入しました。
get_file_id(file_name)

print("5")
```
1,2,3,4,5と表示されていればInstagramに投稿されているはずです。
表示されなければ、失敗しているのでコードを見直すことをお勧めします。

# 終わりに
初めてまともなものを作ることができました。
これからも続けていきたいですね。

参考にさせていただいたサイトを以下に列挙していきます。

https://tkstock.site/2023/03/08/【2023年版】instagram-graph-apiのアクセストークンを取得する方/

https://quattroinc.jp/blog/52/#toc-6

https://enjoy-life-fullest.com/2024/01/10/pythonで実現するインスタグラム画像自動アップロー/

https://qiita.com/sey323/items/875c0ab1585044772ab2

https://www.youtube.com/watch?v=tamT_iGoZDQ&t=374s

ここまで読んでいただきありがとうございました。