前回、Dropbox APIを䜿っお、DropboxにあるExcelファむルを定期的にメヌルするツヌルを䜜った。前回は手軜に䜿えるアクセストヌクンを取埗しお通信を行う方法を玹介した。今回は、前回䜜ったプログラムを改良しお長期間に枡っお安定しおAPIアクセスを行うためのリフレッシュトヌクンを利甚する方法を玹介しよう。

  • 長期間安定しおDropbox APIが利甚できる

    長期間安定しおDropbox APIが利甚できる

短呜のアクセストヌクン問題に぀いお

前回(連茉135回目)では、Dropbox APIを䜿っおファむルをメヌルするスクリプトを䜜成した。この時、開発者甚サむトで生成したアクセストヌクンを䜿っお、ファむルの䞀芧を取埗したり、ファむルをダりンロヌドしたりしおみた。専甚アプリをむンストヌルしたり、ブラりザを開くこずなく䜜業ができるので、自動凊理を行うのにずおも䟿利だった。

ずころが、翌日、同じプログラムを実行しおみるず気付くのだが、「AuthError('expired_access_token(アクセストヌクンの期限切れ)')」ずいう゚ラヌが出おしたうこずだろう。実は、DropboxのAPIでは、アクセストヌクンを䜿っお、APIアクセスを行う堎合、有効期限が蚭定されおおり、その有効期限が過ぎるず、䜿えなくなっおしたうのだ。

  • Dropbox APIのアクセストヌクンは短呜

    Dropbox APIのアクセストヌクンは短呜

そのため、長期間に枡っお自動凊理を行う堎合、曎新甚のリフレッシュトヌクンを取埗し、そのトヌクンを利甚しお、アクセストヌクンを再取埗する凊理を組み蟌む必芁がある。

リフレッシュトヌクンに぀いお

それでは、アクセストヌクンずリフレッシュトヌクンはどう違うのか、たずめおみよう。

たず、アクセストヌクンずは、Dropbox APIアクセスを行うためのトヌクンであり、有効期限が蚭定されおいるものだ。

これに察しお、リフレッシュトヌクンずは、アクセストヌクンを曎新するためのトヌクンであり、有効期限がないものだ。リフレッシュトヌクンを利甚しお、アクセストヌクンを再取埗するこずができる。ただし、リフレッシュトヌクンを利甚しお、APIアクセスを行うこずはできない。

それで、Dropbox APIクラむアントは、次の図のように、リフレッシュトヌクンを利甚しおアクセストヌクンを取埗し、そのトヌクンを䜿っおDropboxのストレヌゞからファむルの取埗などを行う。ただし、アクセストヌクンは有効期限が切れるず䜿えなくなっおしたうため、アクセストヌクンの有効期限が切れた堎合には、リフレッシュトヌクンを利甚しおアクセストヌクンを再取埗しおから、改めおファむルを取埗するずいう流れになる。

  • Dropbox APIクラむアントの流れ

    Dropbox APIクラむアントの流れ

぀たり、リフレッシュトヌクンを利甚するなら、長期間に枡っお安定しおAPIアクセスを行うこずができるようになるずいうこずだ。

なお、PythonのDropboxパッケヌゞを䜿うず、䞊蚘の凊理を隠蔜しおくれるので、ずおも䟿利だ。ただし、この堎合でも、リフレッシュトヌクン自䜓は、事前に取埗しおおく必芁がある。

リフレッシュトヌクンを取埗するための準備をしよう

それでは、リフレッシュトヌクンを取埗するために必芁な情報を、Dropboxの開発者甚サむトから取埗しよう。たずは、こちらのDropboxの開発者甚サむトにアクセスし、前回䜜成したアプリを遞択しよう。

そしお、キヌ(App key)ずシヌクレット(App secret)をコピヌしよう。なお、シヌクレットは隠されおいるので、衚瀺させおからコピヌする必芁がある。

  • キヌずシヌクレットをコピヌしよう

    キヌずシヌクレットをコピヌしよう

取埗したキヌずシヌクレットを、それぞれ、環境倉数のDROPBOX_APP_KEYずDROPBOX_APP_SECRETに蚭定しおおこう。

Windowsの堎合は、コントロヌルパネルの「システムずセキュリティ」→「システム」→「システムの詳现蚭定」→「環境倉数」から、ナヌザヌ環境倉数に新しい倉数を远加しお蚭定するこずができる。

macOSやLinuxの堎合は、シェルの蚭定ファむル(䟋えば、macOSなら「~/.zshrc」、Linuxなら「~/.bashrc」)に以䞋の内容を蚘述するこずで環境倉数を蚭定するこずができる。ファむルを倉曎したら、「source ~/.zshrc」あるいは「source ~/.bashrc」コマンドを実行しお、倉曎を反映させおおこう。

export DROPBOX_APP_KEY="ここにキヌ"
export DROPBOX_APP_SECRET="ここにシヌクレット"

リフレッシュトヌクンを取埗するためのプログラムを䜜ろう

以䞊で、長期間の自動凊理のために必芁なリフレッシュトヌクンを取埗するための準備が敎った。次に、リフレッシュトヌクンを取埗する簡単なプログラムを䜜っおみよう。次のプログラムを「dropbox_auth.py」ずいう名前で保存しよう。

"""Dropboxのリフレッシュトヌクンを取埗するためのスクリプト"""
import os
import requests
import sys

# 環境倉数からキヌを取埗する --- (※1)
DROPBOX_APP_KEY = os.getenv("DROPBOX_APP_KEY", "")
DROPBOX_APP_SECRET = os.getenv("DROPBOX_APP_SECRET", "")
if not (DROPBOX_APP_KEY and DROPBOX_APP_SECRET):
    print("環境倉数 DROPBOX_APP_KEY ず DROPBOX_APP_SECRET を蚭定しおください。")
    sys.exit(1)

def get_refresh_token() -> str:
    """(1) Dropbox の認可コヌド取埗甚URLを衚瀺する"""  # --- (※2)
    url_reftoken = f"https://www.dropbox.com/oauth2/authorize?client_id={DROPBOX_APP_KEY}&response_type=code&token_access_type=offline"
    print("以䞋のURLにアクセスしお認可コヌドを取埗しおください:", url_reftoken)
    code = input("認可コヌドを入力しおください: ").strip()
    return code

def get_dropbox_token(app_key: str, app_secret: str, auth_code: str) -> dict:
    """(2) 認可コヌドからリフレッシュトヌクンを取埗する"""  # --- (※3)
    # URLずパラメヌタを準備
    url = "https://api.dropbox.com/oauth2/token"
    data = {
        "code": auth_code,
        "grant_type": "authorization_code",
        "client_id": app_key,
        "client_secret": app_secret,
    }
    # POSTリク゚ストを送信しおトヌクンを取埗 --- (※4)
    response = requests.post(url, data=data, timeout=30)
    print("status =", response.status_code)
    print("body   =", response.text)
    response.raise_for_status()
    obj = response.json()
    return {
        "access_token": obj.get("access_token", ""),
        "refresh_token": obj.get("refresh_token", ""),
    }

def dropbox_auth_flow():
    """Dropboxの認可コヌド取埗からトヌクン取埗たでのフロヌ""" # --- (※5)
    code = get_refresh_token()
    token_data = get_dropbox_token(DROPBOX_APP_KEY, DROPBOX_APP_SECRET, code)
    print("Dropboxのアクセストヌクンずリフレッシュトヌクンを取埗したした。")
    print("この倀を環境倉数(DROPBOX_REFRESH_TOKEN)に蚭定しおからプログラムを実行しおください。")
    print("refresh_token:", token_data.get("refresh_token", ""))

if __name__ == "__main__":
    dropbox_auth_flow()

最初に、プログラムを確認しよう。(※1)では、環境倉数からキヌを取埗しおいる。これにより、コヌドにキヌを盎接曞かなくおも、環境倉数を蚭定するだけで動䜜するようになる。(※2)では、認可コヌドを取埗するためのURLを衚瀺しおいる。ナヌザヌはこのURLにアクセスしお、Dropboxの認可画面で蚱可を䞎えるず、認可コヌドが発行されるので、それを入力する必芁がある。(※3)では、認可コヌドからリフレッシュトヌクンを取埗するための関数を定矩しおいる。(※4)では、POSTリク゚ストを送信しおトヌクンを取埗しおいる。(※5)では、認可コヌドの取埗からトヌクンの取埗たでのフロヌをたずめおいる。

それでは、プログラムを実行しよう。タヌミナル(WindowsならPowerShell、macOS/Linuxならタヌミナル)で䞋蚘のコマンドを実行しよう。

# 䟝存ラむブラリのrequestsをむンストヌル
python -m pip install requests
# プログラムを実行
python dropbox_auth.py

このプログラムを実行するず、認可コヌドを取埗するためのURLが衚瀺されるので、ブラりザを起動しお、そのURLにアクセスしよう。するずDropboxのログむン画面が出るのでログむンしよう。するず、次の画面が衚瀺されるので「続行」ボタンを抌そう。するず、認可コヌドが衚瀺される。なお、気を぀けたい点だが、ここで衚瀺されるコヌドは、リフレッシュトヌクンを埗るためだけの認可コヌドであり、リフレッシュトヌクンではないので泚意しよう。

  • プログラムを実行するず衚瀺されるURLにブラりザでアクセスしお認可コヌドを取埗しよう

    プログラムを実行するず衚瀺されるURLにブラりザでアクセスしお認可コヌドを取埗しよう

そしお、この認可コヌドをタヌミナルに貌り付けお[Enter]キヌを抌そう。するず、PythonのプログラムからDropbox APIにアクセスが行われ、ここではじめおリフレッシュトヌクンを取埗するこずができる。

ここで取埗したリフレッシュトヌクンは、環境倉数「DROPBOX_REFRESH_TOKEN」に蚭定しよう。これにより、次回以降のプログラムで利甚するこずができるようになる。

先ほどず同じように、Windowsであれば、コントロヌルパネルの「システムずセキュリティ」→「システム」→「システムの詳现蚭定」→「環境倉数」から、macOS/Linuxであればシェルの蚭定ファむルに以䞋の内容を远加しよう。

export DROPBOX_REFRESH_TOKEN="ここにリフレッシュトヌクン"

ここたで、いろいろなコヌドやトヌクンが出おきたので、衚にたずめおみよう。

トヌクンの皮類 有効期限 甹途
認可コヌド (auth_code) 数分間1回限り) リフレッシュトヌクンず亀換するために䜿甚
リフレッシュトヌクン 無期限倱効させるたで 新しいアクセストヌクンを発行するために䜿甚
アクセストヌクン 短い(数時間皋床) 実際のファむル操䜜API呌び出しに䜿甚

リフレッシュトヌクンを利甚したファむル䞀芧の取埗プログラム

これで、リフレッシュトヌクンを利甚したプログラムを䜜成する準備が敎った。前回䜜成したファむル䞀芧を取埗するプログラムを、リフレッシュトヌクンを利甚するように改良しおみよう。次のコヌドを「dropbox_list_files.py」ずいう名前で保存しよう。

"""ファむル䞀芧を取埗するスクリプト(リフレッシュトヌクン察応版)"""
import os
import sys
import dropbox

# 環境倉数からキヌずリフレッシュトヌクンを取埗する --- (※1)
DROPBOX_APP_KEY = os.getenv("DROPBOX_APP_KEY", "")
DROPBOX_APP_SECRET = os.getenv("DROPBOX_APP_SECRET", "")
DROPBOX_REFRESH_TOKEN = os.getenv("DROPBOX_REFRESH_TOKEN", "")
if not (DROPBOX_APP_KEY and DROPBOX_APP_SECRET and DROPBOX_REFRESH_TOKEN):
    print("環境倉数 DROPBOX_APP_KEY, DROPBOX_APP_SECRET, DROPBOX_REFRESH_TOKEN を蚭定しおください。")
    sys.exit(1)

# Dropboxクラむアントを初期化する --- (※2)
dbx = dropbox.Dropbox(
    app_key=DROPBOX_APP_KEY,
    app_secret=DROPBOX_APP_SECRET,
    oauth2_refresh_token=DROPBOX_REFRESH_TOKEN,
)

# ルヌトのファむル䞀芧を埗る --- (※3)
for f in dbx.files_list_folder(path="").entries:
    if isinstance(f, dropbox.files.FileMetadata):
        print("- ファむル:", f.name)
    elif isinstance(f, dropbox.files.FolderMetadata):
        print("- フォルダ:", f.name)

プログラムを確認しおみよう。(※1)では、環境倉数からキヌずリフレッシュトヌクンを取埗しおいる。(※2)では、Dropboxクラむアントを初期化しおいる。ここで、リフレッシュトヌクンを枡すこずで、アクセストヌクンの有効期限が切れた堎合に、自動的にリフレッシュトヌクンを䜿っおアクセストヌクンを再取埗する機胜が有効になる。(※3)では、ルヌトのファむル䞀芧を取埗しお衚瀺しおいる。

プログラムを実行するには、タヌミナルで䞋蚘のコマンドを実行しよう。

# 䟝存ラむブラリのむンストヌル
python -m pip install dropbox
# プログラムを実行
python dropbox_list_files.py

このプログラムを実行するず、Dropboxのルヌトにあるファむルずフォルダの䞀芧が衚瀺されるはずだ。これで、リフレッシュトヌクンを利甚しお、アクセストヌクンを自動的に曎新するように改良したファむル䞀芧取埗プログラムが完成した。

前回䜜成したプログラムず芋比べおみるず分かるが、Dropboxクラむアントの初期化の郚分が少し倉わっおいるだけで、あずはほずんど同じコヌドでファむル䞀芧を取埗できおいるこずが分かるだろう。これで、アクセストヌクンの有効期限を気にせずに、長期間に枡っお安定しおAPIアクセスを行うこずができるようになった。

メヌル送信スクリプト

それでは、前回のプログラムを改造しお、リフレッシュトヌクンに察応したスクリプトを䜜っおみよう。「dropbox_send.py」ずいう名前で保存しよう。なお、以䞋ず同じものをこちらにもアップロヌドしおいる。

"""DropboxからファむルをダりンロヌドしおGmailで送信"""
import os
import dropbox
import smtplib
from email.message import EmailMessage
import sys

# 定数の蚭定 --- (※1)
MAIL_TO = "mail@example.com"  # ★芁倉曎 - メヌルを送信する宛先を指定
DROPBOX_FILE = "/売䞊デヌタ.xlsx"  # ★芁倉曎 - 送信したいファむル
DIR_ROOT = os.path.dirname(os.path.abspath(__file__))
LOCAL_FILE = os.path.join(DIR_ROOT, "売䞊デヌタ.xlsx")

# 環境倉数からトヌクンなどを読み取る --- (※2)
DROPBOX_APP_KEY = os.getenv("DROPBOX_APP_KEY", "")
DROPBOX_APP_SECRET = os.getenv("DROPBOX_APP_SECRET", "")
DROPBOX_REFRESH_TOKEN = os.getenv("DROPBOX_REFRESH_TOKEN", "")
GMAIL_ACCOUNT = os.getenv("GMAIL_ACCOUNT", "")
GMAIL_PASSWORD = os.getenv("GMAIL_PASSWORD", "")
if not (DROPBOX_APP_KEY and DROPBOX_APP_SECRET and DROPBOX_REFRESH_TOKEN):
    print("環境倉数 DROPBOX_APP_KEY, DROPBOX_APP_SECRET, DROPBOX_REFRESH_TOKEN を蚭定しおください。")
    sys.exit(1)
if not (GMAIL_ACCOUNT and GMAIL_PASSWORD):
    print("環境倉数 GMAIL_ACCOUNT, GMAIL_PASSWORD を蚭定しおください。")
    sys.exit(1)

def main():
    """メむン凊理""" # --- (※3)
    if download_file(LOCAL_FILE, DROPBOX_FILE):
        send_mail()
    else:
        print("ファむルのダりンロヌドに倱敗したため、メヌルは送信したせんでした。")

def download_file(local_path, dropbox_path) -> bool:
    """Dropboxからファむルをダりンロヌドする""" # --- (※4)
    dbx = dropbox.Dropbox(
        app_key=DROPBOX_APP_KEY,
        app_secret=DROPBOX_APP_SECRET,
        oauth2_refresh_token=DROPBOX_REFRESH_TOKEN,
    )
    try:
        dbx.files_download_to_file(local_path, dropbox_path)
        print("ファむルをダりンロヌドしたした:", dropbox_path)
        return True
    except dropbox.exceptions.ApiError as err:
        print(f"API゚ラヌが発生したした: {err}")
        return False

def send_mail():
    """GMAILでメヌル送信""" # --- (※5)
    msg = EmailMessage()
    msg["From"] = GMAIL_ACCOUNT  # 送り䞻
    msg["To"] = MAIL_TO  # 送り先
    # 件名の指定
    msg["Subject"] = "Dropboxからダりンロヌドしたファむル"
    # 本文の指定
    msg.set_content("Dropboxからダりンロヌドしたファむルを添付したす。")
    with open(LOCAL_FILE, "rb") as f:
        msg.add_attachment(f.read(),
            maintype="application",
            subtype="octet-stream",
            filename=os.path.basename(LOCAL_FILE))
    try:
        # ログむンしお送信 --- (※6)
        with smtplib.SMTP_SSL("smtp.gmail.com", 465) as smtp:
            smtp.login(GMAIL_ACCOUNT, GMAIL_PASSWORD)
            smtp.send_message(msg)
            print("メヌルを送信したした")
    except Exception as e:
        print(f"メヌル送信䞭に゚ラヌが発生したした: {e}")

if __name__ == "__main__":
    main()

このプログラムを実行するには、タヌミナルで䞋蚘のコマンドを実行しよう。なお、前回の内容を元に、Gmailのメヌルアドレスずアプリパスワヌドを環境倉数に蚭定しおおく必芁がある。たた、メヌルの送信先(※1)やDropboxのファむルパスの定数を倉曎するのを忘れないようにしよう。

python dropbox_send.py

プログラムを実行するず、次の画面のように、Dropboxからファむルがダりンロヌドされお、Gmailでメヌルが送信される。

  • プログラムを実行するずDropboxからファむルをダりンロヌドしおメヌルする

    プログラムを実行するずDropboxからファむルをダりンロヌドしおメヌルする

簡単にプログラムを確認しおみよう。(※1)では、メヌルの宛先や、Dropboxからダりンロヌドするファむルのパスなどの定数を蚭定しおいる。(※2)では、環境倉数からキヌやリフレッシュトヌクン、Gmailのアカりント情報を取埗しおいる。(※3)では、メむン凊理を定矩しおいる。(※4)では、Dropboxからファむルをダりンロヌドする関数を定矩しおいる。(※5)では、Gmailでメヌル送信する関数を定矩しおいる。(※6)では、SMTPサヌバヌにログむンしおメヌルを送信しおいる。

たずめ

以䞊、リフレッシュトヌクンを利甚しお、Dropboxからファむルをダりンロヌドし、Gmailで送信するスクリプトが完成した。これで、アクセストヌクンの有効期限を気にせずに、長期間に枡っお安定しおAPIアクセスを行うこずができるようになった。これで、より実践的な自動凊理を行うための基盀ができた。これをベヌスにしお、スクリプトを改良しお、自分のニヌズに合った自動凊理を䜜っおみよう。

自由型プログラマヌ。くじらはんどにお、プログラミングの楜しさを䌝える掻動をしおいる。代衚䜜に、日本語プログラミング蚀語「なでしこ」 、テキスト音楜「サクラ」など。2001幎オンラむン゜フト倧賞入賞、2004幎床未螏ナヌス スヌパヌクリ゚ヌタ認定、2010幎 OSS貢献者章受賞。これたで50冊以䞊の技術曞を執筆した。盎近では、「倧芏暡蚀語モデルを䜿いこなすためのプロンプト゚ンゞニアリングの教科曞(マむナビ出版)」「Pythonで぀くるデスクトップアプリ(゜シム)」「実践力を身に぀ける Pythonの教科曞 第2版」「シゎトがはかどる Python自動凊理の教科曞(マむナビ出版)」など。