[Python] Dropbox API でファイル情報を取得(sdk)

## 今度はPythonで with SDK
以前、[PHPでDropbox APIを使った記事](https://qiita.com/tksnino/items/c39b509494cc7f05954a)を書きましたが、そのPython版です。
Python はほぼビギナー！

## 準備
developerサイトでapp登録するところまでは同じです。

SDKをインストールします。

```
$ pip install dropbox
```
簡単～。

SDKのドキュメントは[こちら](https://dropbox-sdk-python.readthedocs.io/en/latest/index.html)

## インスタンス生成
【更新】[リフレッシュトークンを利用したアクセストークン取得形式](https://navi.dropbox.jp/dropbox-api-migrating-app-permissions-and-access-tokens)に対応
【更新2】[チームスペース形式](https://navi.dropbox.jp/guide-to-adding-support-for-team-space)に対応
```python
import dropbox
import requests

APP_KEY = 'XXXXXXXX'
APP_SECRET = 'XXXXXXXX'
REFRESH_TOKEN = 'xxxxxxxxxxxxxxxx'

#「リフレッシュトークン」を利用してアクセストークン取得
auth = (APP_KEY, APP_SECRET)
param = {'grant_type': 'refresh_token', 'refresh_token': REFRESH_TOKEN}

res = requests.post('https://api.dropboxapi.com/oauth2/token', auth=auth, data=param)

if res.status_code != 200:
    res.raise_for_status()

token = res.json()

dbx = dropbox.Dropbox(token['access_token'])

#「チームスペース」対応
root_namespace_id = dbx.users_get_current_account().root_info.root_namespace_id
dbx = dbx.with_path_root(dropbox.common.PathRoot.root(root_namespace_id))
```

これを使って各種メソッドを呼んでいきます。

## ファイル一覧を取得する
HTTPリクエストの

- [files/list_folder](https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder)
- [files/list_folder/continue](https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-continue)

これに対応するのが

- [files_list_folder](https://dropbox-sdk-python.readthedocs.io/en/latest/api/dropbox.html#dropbox.dropbox.Dropbox.files_list_folder)
- [files_list_folder_continue](https://dropbox-sdk-python.readthedocs.io/en/latest/api/dropbox.html#dropbox.dropbox.Dropbox.files_list_folder_continue)

```python
res = dbx.files_list_folder('指定ディレクトリ', recursive=True)
```
返り値はこうなっていて、
`res.entries`：結果配列
`res.has_more`：続きがあるか
`res.cursor`：has_more=Trueの場合、files_list_folder_continueのパラメータとなる

再帰的に呼び出すとこんなイメージです。

```python:指定ディレクトリ以下すべて取得
def __get_files_recursive(res):
    for entry in res.entries:
        print(entry.path_display)

    if (res.has_more):
        res2 = dbx.files_list_folder_continue(res.cursor)
        __get_files_recursive(res2)

res = dbx.files_list_folder('指定ディレクトリ', recursive=True)
__get_files_recursive(res)
```

結果配列にはフォルダもファイルも含まれていて、HTTPリクエストだと`.tag`というプロパティにフォルダかファイルか持っていますが、
Dropbox for pythonだと要素が`FolderMetadata`classか`FileMetadata`classかになっています。

```python:ファイルのみ
for entry in res.entries:
    ins = type(entry)
    if ins is not dropbox.files.FileMetadata: #ファイル以外（＝フォルダ）はスキップ
        continue

    print(entry.path_display)
```

## 指定ファイルの共有リンクを取得する
同じくHTTPリクエストの

- [sharing/list_shared_links](https://www.dropbox.com/developers/documentation/http/documentation#sharing-list_shared_links)
- [sharing/create_shared_link_with_settings](https://www.dropbox.com/developers/documentation/http/documentation#sharing-create_shared_link_with_settings)

これに対応するのが

- [sharing_list_shared_links](https://dropbox-sdk-python.readthedocs.io/en/latest/api/dropbox.html#dropbox.dropbox.Dropbox.sharing_list_shared_links)
- [sharing_create_shared_link_with_settings](https://dropbox-sdk-python.readthedocs.io/en/latest/api/dropbox.html#dropbox.dropbox.Dropbox.sharing_create_shared_link_with_settings)

```python:共有リンク取得
    def __get_shared_link(path):
        links = dbx.sharing_list_shared_links(path=path, direct_only=True).links

        if links is not None:
            for link in links:
                return link.url #1件目を返す
```

```python:共有リンク発行
    def __create_shared_link(path):
        setting = dropbox.sharing.SharedLinkSettings(requested_visibility=dropbox.sharing.RequestedVisibility.public)
        link = dbx.sharing_create_shared_link_with_settings(path=path, settings=setting)

        return link.url
```

`settings`に渡すパラメータは[`SharedLinkSettings`](https://dropbox-sdk-python.readthedocs.io/en/latest/api/sharing.html#dropbox.sharing.SharedLinkSettings)classインスタンスになります。

## まとめ
class化してみます。
`viewFiles`を呼ぶと、ファイルパスと共有リンク（なければ公開で発行）を出力します。

【更新】リフレッシュトークンを利用したアクセストークン取得形式に対応
【更新2】チームスペース形式に対応
```python:class
import dropbox
import requests

class MyDropbox():
    APP_KEY = 'XXXXXXXX'
    APP_SECRET = 'XXXXXXXX'
    REFRESH_TOKEN = 'xxxxxxxxxxxxxxxx'
    DB_ROOT_DIR = '/指定フォルダ'

    def __init__(self):
        #「リフレッシュトークン」からアクセストークン取得
        self.dbx = dropbox.Dropbox(self.__get_access_token())

        #「チームスペース」対応
        root_namespace_id = self.dbx.users_get_current_account().root_info.root_namespace_id
        self.dbx = self.dbx.with_path_root(dropbox.common.PathRoot.root(root_namespace_id))

    def __get_access_token():
        auth = (self.APP_KEY, self.APP_SECRET)
        param = {'grant_type': 'refresh_token', 'refresh_token': self.REFRESH_TOKEN}

        res = requests.post('https://api.dropboxapi.com/oauth2/token', auth=auth, data=param)

        if res.status_code != 200:
            res.raise_for_status()

        token = res.json()

        return token['access_token']

    def viewFiles(self):
        res = self.dbx.files_list_folder(self.DB_ROOT_DIR, recursive=True)

        self.__get_files_recursive(res)

    def __get_files_recursive(self, res):
        for entry in res.entries:
            ins = type(entry)
            if ins is not dropbox.files.FileMetadata: #ファイル以外（＝フォルダ）はスキップ
                continue

            link = self.__get_shared_link(entry.path_lower)
            if bool(link):
                print(entry.path_display)
                print(link)

        if res.has_more:
            res2 = self.dbx.files_list_folder_continue(res.cursor)
            self.__get_files_recursive(res2)

    def __get_shared_link(self, path):
        links = self.dbx.sharing_list_shared_links(path=path, direct_only=True).links

        if links is not None:
            for link in links:
                return link.url #1件目

        return self.__create_shared_link(path)

    def __create_shared_link(self, path):
        setting = dropbox.sharing.SharedLinkSettings(requested_visibility=dropbox.sharing.RequestedVisibility.public)
        link = self.dbx.sharing_create_shared_link_with_settings(path=path, settings=setting)

        return link.url
```

## 参考
- [DropboxへAPIを使ってアクセスしてみる](http://tatata.hatenablog.jp/entry/2017/02/19/213408)
- [Dropbox for Python Documentation](https://dropbox-sdk-python.readthedocs.io/en/latest/index.html)
- [WindowsにPythonをインストール + pip + virtualenv](https://qiita.com/zkangaroo/items/4bb48b405e1651f2e273)
- [【入門者向け】Pythonのクラスを習得する方法を詳しく解説！](https://blog.codecamp.jp/python-class)
- [Pythonのimportについてまとめる](https://qiita.com/suzuki-hoge/items/f951d56290617df4279e)
- [Python で定数を定義する](https://maku77.github.io/python/syntax/const.html)
- [Pythonで型を取得・判定するtype関数, isinstance関数](https://note.nkmk.me/python-type-isinstance/)