Python, PyDriveでGoogle Driveのファイルのリストを作成

Posted: | Tags: Python, PyDrive

PyDriveを使うとPythonからGoogleドライブを簡単に操作できる。

ここでは、Googleドライブ上のファイルやフォルダのリスト作成について説明する。検索条件を指定して該当するファイルやフォルダを抽出してリスト化することも可能。

  • Googleドライブのファイル・フォルダのリスト作成
    • ListFile()の基本的な使い方
    • ソートの基準を指定
    • ページングとmaxResults
  • 検索条件を指定
    • 条件指定の基本
    • ファイルのみ・フォルダのみ: mimeType
    • ファイル名・フォルダ名: title
    • 指定のフォルダに含まれるファイル・フォルダ: parents
    • ゴミ箱: trashed
  • リストのファイル・フォルダを一括処理(削除・ダウンロードなど)
  • リストをCSVで保存(csvモジュール使用)
  • リストをpandas.DataFrameに変換
    • CSVで保存
    • 親フォルダをパス形式(xxx/yyy/zzz)で表す

PyDriveの基本的な使い方(利用登録や認証、ファイルのダウンロード、アップロード、削除など)は以下の記事を参照。

サンプルコードでは、以下の構成のファイル・フォルダを例とする。

root
├── dir1
│   ├── file11
│   ├── file12
│   └── subdir1
│       └── file111
├── dir2
│   └── file21
├── file1
└── file2

なお、以下はPyDriveバージョン1.3.1の情報。

Googleドライブのファイル・フォルダのリスト生成

ListFile()の基本的な使い方

ListFile()GoogleDriveFileListオブジェクトを生成し、さらにGetList()でリスト化する。

GoogleDriveFileオブジェクトを要素とするリストが取得できる。

from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive

gauth = GoogleAuth()
gauth.LocalWebserverAuth()

drive = GoogleDrive(gauth)

file_list = drive.ListFile().GetList()

print(type(file_list))
# <class 'list'>

print(len(file_list))
# 9

print(type(file_list[0]))
# <class 'pydrive.files.GoogleDriveFile'>

for f in file_list:
    print(f['title'], '   \t', f['id'])
# file21         1hJ7WQDiZyefLCQsDBE8J5-Nz847Vp670
# dir2       1wmWf1fhJTgi3X1Y07t2T8KYm4GtYEgxP
# file111        1789K-xv4ereCSOKrDKiAQI5bBypjqtIB
# subdir1        1k154tW38rbXxj88fjV8IZVkgB3EZ7g2M
# file12         1QRndXjXG9p2MEnVuotGd-eCNE2vNpkfp
# file11         1nCotx1q3DEaRWQOqbPqbPS1rGMWXRt8q
# dir1       1qQEUlPaIiDR_WI3hNf25rXUCHrlZIK_y
# file2      1ewv3ht8J2T3oMFmBgpqeS8zWHyHJsEwg
# file1      1WY5-WfWxj-ovefoE1dzWEfzgt8wtfbwY

デフォルトではすべてのファイル・フォルダが対象となる。大量にファイル・フォルダがある状態だとリストの生成に時間がかかるので注意。16,000個ぐらいあるアカウントだと1分程度かかった。

条件で絞る方法については後述。

ソートの基準を指定

ListFile()ではGoogle Drive APIのFiles: listを呼んでおり、そのパラメータを辞書(dict)で引数に指定できる。

例えば、ソートの基準を決めるorderByを設定する例は以下の通り。降順にしたい場合はdescを付ける。

for f in drive.ListFile({'orderBy': 'title'}).GetList():
    print(f['title'], '   \t', f['id'])
# dir1       1qQEUlPaIiDR_WI3hNf25rXUCHrlZIK_y
# dir2       1wmWf1fhJTgi3X1Y07t2T8KYm4GtYEgxP
# file1      1WY5-WfWxj-ovefoE1dzWEfzgt8wtfbwY
# file11         1nCotx1q3DEaRWQOqbPqbPS1rGMWXRt8q
# file111        1789K-xv4ereCSOKrDKiAQI5bBypjqtIB
# file12         1QRndXjXG9p2MEnVuotGd-eCNE2vNpkfp
# file2      1ewv3ht8J2T3oMFmBgpqeS8zWHyHJsEwg
# file21         1hJ7WQDiZyefLCQsDBE8J5-Nz847Vp670
# subdir1        1k154tW38rbXxj88fjV8IZVkgB3EZ7g2M

for f in drive.ListFile({'orderBy': 'title desc'}).GetList():
    print(f['title'], '   \t', f['id'])
# subdir1        1k154tW38rbXxj88fjV8IZVkgB3EZ7g2M
# file21         1hJ7WQDiZyefLCQsDBE8J5-Nz847Vp670
# file2      1ewv3ht8J2T3oMFmBgpqeS8zWHyHJsEwg
# file12         1QRndXjXG9p2MEnVuotGd-eCNE2vNpkfp
# file111        1789K-xv4ereCSOKrDKiAQI5bBypjqtIB
# file11         1nCotx1q3DEaRWQOqbPqbPS1rGMWXRt8q
# file1      1WY5-WfWxj-ovefoE1dzWEfzgt8wtfbwY
# dir2       1wmWf1fhJTgi3X1Y07t2T8KYm4GtYEgxP
# dir1       1qQEUlPaIiDR_WI3hNf25rXUCHrlZIK_y

Google Drive APIのパラメータについては以下の公式ドキュメントを参照。なお、バージョン1.3.1時点ではPyDriveはGoogle Drive APIのv2を使用しているので注意。

検索条件を指定するqについては後述。

ページングとmaxResults

パラメータmaxResultsを設定して、一度に決まった件数ずつ取得することも可能。

for i, f_list in enumerate(drive.ListFile({'maxResults': 3})):
    print('=====', 'page', i)
    for f in f_list:
        print(f['title'], '   \t', f['id'])
# ===== page 0
# file21         1hJ7WQDiZyefLCQsDBE8J5-Nz847Vp670
# dir2       1wmWf1fhJTgi3X1Y07t2T8KYm4GtYEgxP
# file111        1789K-xv4ereCSOKrDKiAQI5bBypjqtIB
# ===== page 1
# subdir1        1k154tW38rbXxj88fjV8IZVkgB3EZ7g2M
# file12         1QRndXjXG9p2MEnVuotGd-eCNE2vNpkfp
# file11         1nCotx1q3DEaRWQOqbPqbPS1rGMWXRt8q
# ===== page 2
# dir1       1qQEUlPaIiDR_WI3hNf25rXUCHrlZIK_y
# file2      1ewv3ht8J2T3oMFmBgpqeS8zWHyHJsEwg
# file1      1WY5-WfWxj-ovefoE1dzWEfzgt8wtfbwY

これまでの例のようにmaxResultsを設定せずにGetList()を使うと、自動的に全件を取得してリストを返してくれる。

for f in drive.ListFile().GetList():
    print(f['title'], '   \t', f['id'])
# file21         1hJ7WQDiZyefLCQsDBE8J5-Nz847Vp670
# dir2       1wmWf1fhJTgi3X1Y07t2T8KYm4GtYEgxP
# file111        1789K-xv4ereCSOKrDKiAQI5bBypjqtIB
# subdir1        1k154tW38rbXxj88fjV8IZVkgB3EZ7g2M
# file12         1QRndXjXG9p2MEnVuotGd-eCNE2vNpkfp
# file11         1nCotx1q3DEaRWQOqbPqbPS1rGMWXRt8q
# dir1       1qQEUlPaIiDR_WI3hNf25rXUCHrlZIK_y
# file2      1ewv3ht8J2T3oMFmBgpqeS8zWHyHJsEwg
# file1      1WY5-WfWxj-ovefoE1dzWEfzgt8wtfbwY

公式ドキュメントの記述は以下。

GetList()

Get list of API resources.
If ‘maxResults’ is not specified, it will automatically iterate through every resources available. Otherwise, it will make API call once and update ‘pageToken’. pydrive package - pydrive.apiattr.ApiResourceList.GetList — PyDrive 1.2.1 documentation

ソースコードは以下。maxResults=1000としてイテレートしている模様。

ファイルやフォルダを少しずつ処理したいというような場合でなければ、maxResultsを設定せずに全件取得すればよい。

検索条件を指定

条件指定の基本

検索条件を指定してリスト化するには、qに検索条件を表す文字列を設定する。

例えば、名前(ファイル名・フォルダ名)がfile1から始まるファイル・フォルダを抽出する例。

for f in drive.ListFile({'q': 'title contains "file1"'}).GetList():
    print(f['title'], '  \t', f['id'])
# file111        1bfc7jlDobxuoyZeF8CDyXlN66zl84Ech
# file12     1iv9UB1pi65MqfFmqYwBoz3AfZbKr2jLx
# file11     15sy12U3d845pXjLaEWww4VLO9Vs7C4J9
# file1      1z1vv7Zd1k14JebVghq8NlVpLWmFhZybo

andorで複数条件を組み合わせることも可能。否定のnotも使える。上の条件に、親フォルダがルートではない、という条件を追加した例。

for f in drive.ListFile({'q': 'title contains "file1" and not "root" in parents'}).GetList():
    print(f['title'], '  \t', f['id'])
# file111        1bfc7jlDobxuoyZeF8CDyXlN66zl84Ech
# file12     1iv9UB1pi65MqfFmqYwBoz3AfZbKr2jLx
# file11     15sy12U3d845pXjLaEWww4VLO9Vs7C4J9

検索条件の文字列(クエリ文字列)で使える演算子および語句(フィールド名)は以下の公式ドキュメントを参照。

なお、クエリ文字列の中でファイル名などの文字列を表す場合はシングルクォーテーション'でもダブルクォーテーション"でもどちらを使ってもいい。以下のどのパターンでもOK。

# 'title contains "file1"'
# "title contains 'file1'"
# 'title contains \'file1\''
# "title contains \"file1\""

Pythonの文字列リテラルにおける引用符の扱いについては以下の記事を参照。

クエリ文字列の例は以下の記事を参照。ファイル名・フォルダ名(title)や更新日時(modifiedDate)などで条件を指定できる。このあとでいくつか具体例を示す。

上述のように、バージョン1.3.1時点ではPyDriveはGoogle Drive APIのv2を使用している。ファイル名・フォルダ名はv2ではtitleだがv3ではnameに変更されているなどの違いがある。v3のGoogle Drive APIのサンプルコードを参考にするとエラーになる可能性があるので注意。

ファイルのみ・フォルダのみ: mimeType

項目がファイルかフォルダかはmimeTypeで判定する。GoogleドライブにおけるフォルダのmimeTypeapplication/vnd.google-apps.folder

等価(〜である)を表す演算子は===ではないので注意。等価でない、は!=

for f in drive.ListFile({'q': 'mimeType = "application/vnd.google-apps.folder"'}).GetList():
    print(f['title'], '   \t', f['id'])
# dir2       1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9
# subdir1        1CpPdnFTdN6DWPz7VBM1pV-RMZfcbvzck
# dir1       1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1

for f in drive.ListFile({'q': 'mimeType != "application/vnd.google-apps.folder"'}).GetList():
    print(f['title'], '  \t', f['id'])
# file21     1o-pFt-9Hrj_z3FmlsQfa8duo_ZfuEh5S
# file111        1bfc7jlDobxuoyZeF8CDyXlN66zl84Ech
# file12     1iv9UB1pi65MqfFmqYwBoz3AfZbKr2jLx
# file11     15sy12U3d845pXjLaEWww4VLO9Vs7C4J9
# file2      1HaHLB19oU_he6LI_6mkQsPr9uH_JK_oz
# file1      1z1vv7Zd1k14JebVghq8NlVpLWmFhZybo

mimeTypeを利用すると、'mimeType = "image/jpeg"'のようにしてJPEG画像のみを抽出したりもできる。

ファイル名・フォルダ名: title

ファイル名・フォルダ名についての条件はtitleを使う。演算子として、=, !=, containsが使える。

for f in drive.ListFile({'q': 'title = "file1"'}).GetList():
    print(f['title'], '  \t', f['id'])
# file1      1z1vv7Zd1k14JebVghq8NlVpLWmFhZybo

for f in drive.ListFile({'q': 'title contains "file1"'}).GetList():
    print(f['title'], '  \t', f['id'])
# file111        1bfc7jlDobxuoyZeF8CDyXlN66zl84Ech
# file12     1iv9UB1pi65MqfFmqYwBoz3AfZbKr2jLx
# file11     15sy12U3d845pXjLaEWww4VLO9Vs7C4J9
# file1      1z1vv7Zd1k14JebVghq8NlVpLWmFhZybo

for f in drive.ListFile({'q': 'title contains "11"'}).GetList():
    print(f['title'], '  \t', f['id'])

containsは前方一致検索なので注意。上のサンプルコードの最後の例のように、途中の文字列にはマッチしない。

The contains operator only performs prefix matching for a title. For example, the title "HelloWorld" would match for title contains 'Hello' but not title contains 'World'. Search query terms | Drive REST API v2 | Google Developers

指定のフォルダに含まれるファイル・フォルダ: parents

親フォルダの判定はparentsin'"<folder_id>" in parents'とする。フォルダ名ではなくIDを指定する必要があるので注意。

dir1_id = drive.ListFile({'q': 'title = "dir1"'}).GetList()[0]['id']

for f in drive.ListFile({'q': '"{}" in parents'.format(dir1_id)}).GetList():
    print(f['title'], '   \t', f['id'])
# subdir1        1CpPdnFTdN6DWPz7VBM1pV-RMZfcbvzck
# file12         1iv9UB1pi65MqfFmqYwBoz3AfZbKr2jLx
# file11         15sy12U3d845pXjLaEWww4VLO9Vs7C4J9

ルートフォルダ(マイフォルダ)はrootという文字列で代用できる。

for f in drive.ListFile({'q': '"root" in parents'}).GetList():
    print(f['title'], '   \t', f['id'])
# dir2       1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9
# dir1       1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1
# file2      1HaHLB19oU_he6LI_6mkQsPr9uH_JK_oz
# file1      1z1vv7Zd1k14JebVghq8NlVpLWmFhZybo

この方法で取得できるのはあくまでも親フォルダが指定したフォルダであるファイル・フォルダのリスト。サブフォルダ(さらに下の階層)まで対象としたい場合は再帰的に処理する必要がある。詳細は以下の記事を参照。

ゴミ箱: trashed

ファイル・フォルダがゴミ箱に入っているかどうかはtrashedで判定する。trashedtruefalseのいずれかの値を持つ。

Googleドライブにおいては、ゴミ箱に入っていても親フォルダparents自体は変わらないため、trashedを設定しないとゴミ箱の中のファイルやフォルダまでリストアップされてしまう。要注意。

file1_id = drive.ListFile({'q': 'title = "file1"'}).GetList()[0]['id']
file1 = drive.CreateFile({'id': file1_id})
file1.Trash()

for f in drive.ListFile({'q': '"root" in parents'}).GetList():
    print(f['title'], '   \t', f['id'])
# file1      1z1vv7Zd1k14JebVghq8NlVpLWmFhZybo
# dir2       1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9
# dir1       1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1
# file2      1HaHLB19oU_he6LI_6mkQsPr9uH_JK_oz

for f in drive.ListFile({'q': '"root" in parents and trashed = false'}).GetList():
    print(f['title'], '   \t', f['id'])
# dir2       1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9
# dir1       1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1
# file2      1HaHLB19oU_he6LI_6mkQsPr9uH_JK_oz

リストのファイル・フォルダを一括処理(削除・ダウンロードなど)

ListFile()で取得できるのはGoogleDriveFileオブジェクトを要素とするリスト。

これまでの例ではprint(f['title'], ...)のようにファイル名などを出力していたが、そこでファイルのダウンロードやリネームなどの処理を行えばよい。

ダウンロードする例。

file_list = drive.ListFile({'q': 'title contains "file1" and trashed = false'}).GetList()

for f in file_list:
    print(f['title'], '  \t', f['id'])
# file1      1z1vv7Zd1k14JebVghq8NlVpLWmFhZybo
# file111        1bfc7jlDobxuoyZeF8CDyXlN66zl84Ech
# file12     1iv9UB1pi65MqfFmqYwBoz3AfZbKr2jLx
# file11     15sy12U3d845pXjLaEWww4VLO9Vs7C4J9

dst_dir = 'dst'

for f in file_list:
    f.GetContentFile(os.path.join(dst_dir, f['title']))

リネームする例。

for f in file_list:
    f['title'] = 'XXX_' + f['title']
    f.Upload()

for f in file_list:
    print(f['title'], '  \t', f['id'])
# XXX_file1      1z1vv7Zd1k14JebVghq8NlVpLWmFhZybo
# XXX_file111        1bfc7jlDobxuoyZeF8CDyXlN66zl84Ech
# XXX_file12     1iv9UB1pi65MqfFmqYwBoz3AfZbKr2jLx
# XXX_file11     15sy12U3d845pXjLaEWww4VLO9Vs7C4J9

削除の場合も同様にTrash()Delete()を使えばよい。

なお、フォルダを削除するとその中のファイルやフォルダも同時に削除されるので、フォルダを丸ごと削除する場合は特にリストを作成する必要はない。

サブフォルダ(さらに下の階層)までまとめてダウンロードやリネームなどをしたい場合は再帰的に処理を行う。以下の記事を参照。

リストをCSVで保存(csvモジュール使用)

取得したリストをCSVとして保存したい場合、2次元のリスト(リストのリスト)を作成する。

import csv
import pprint

from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive

gauth = GoogleAuth()
gauth.LocalWebserverAuth()

drive = GoogleDrive(gauth)

l = [[f['title'], f['id']] for f in drive.ListFile().GetList()]
pprint.pprint(l)
# [['file11', '15sy12U3d845pXjLaEWww4VLO9Vs7C4J9'],
#  ['file12', '1iv9UB1pi65MqfFmqYwBoz3AfZbKr2jLx'],
#  ['file111', '1bfc7jlDobxuoyZeF8CDyXlN66zl84Ech'],
#  ['file1', '1z1vv7Zd1k14JebVghq8NlVpLWmFhZybo'],
#  ['file21', '1o-pFt-9Hrj_z3FmlsQfa8duo_ZfuEh5S'],
#  ['dir2', '1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9'],
#  ['subdir1', '1CpPdnFTdN6DWPz7VBM1pV-RMZfcbvzck'],
#  ['dir1', '1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1'],
#  ['file2', '1HaHLB19oU_he6LI_6mkQsPr9uH_JK_oz']]

この例はファイル名・フォルダ名とIDのみを抽出しているが、その他の情報が必要であれば同じ要領でリストの要素を増やせばよい。次のpandasの例でさらに多くの要素を抽出する例を紹介している。

ヘッダーとなるリストを先頭に追加し、csvモジュールのwriterows()で書き込む。

l.insert(0, ['title', 'id'])

with open('dst/use_csv.csv', 'w') as f:
    writer = csv.writer(f)
    writer.writerows(l)

リストをpandas.DataFrameに変換

取得したデータを処理したい場合、pandasのDataFrameに変換すると便利。

csvモジュールを使う例と同様に、まずは2次元のリスト(リストのリスト)を作成する。ここではより複雑な例を紹介する。

import pprint
import pandas as pd

from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive

gauth = GoogleAuth()
gauth.LocalWebserverAuth()

drive = GoogleDrive(gauth)

l = [[f['title'], f['id'], f['parents'][0]['id'], f['mimeType'], f.get('fileSize')]
     for f in drive.ListFile().GetList()
     if len(f['parents'])]

pprint.pprint(l, width=200)
# [['file11', '15sy12U3d845pXjLaEWww4VLO9Vs7C4J9', '1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1', 'application/octet-stream', '0'],
#  ['file12', '1iv9UB1pi65MqfFmqYwBoz3AfZbKr2jLx', '1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1', 'application/octet-stream', '0'],
#  ['file111', '1bfc7jlDobxuoyZeF8CDyXlN66zl84Ech', '1CpPdnFTdN6DWPz7VBM1pV-RMZfcbvzck', 'application/octet-stream', '0'],
#  ['file1', '1z1vv7Zd1k14JebVghq8NlVpLWmFhZybo', '0AAeKIFCqYN07Uk9PVA', 'application/octet-stream', '0'],
#  ['file21', '1o-pFt-9Hrj_z3FmlsQfa8duo_ZfuEh5S', '1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9', 'application/octet-stream', '0'],
#  ['dir2', '1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9', '0AAeKIFCqYN07Uk9PVA', 'application/vnd.google-apps.folder', None],
#  ['subdir1', '1CpPdnFTdN6DWPz7VBM1pV-RMZfcbvzck', '1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1', 'application/vnd.google-apps.folder', None],
#  ['dir1', '1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1', '0AAeKIFCqYN07Uk9PVA', 'application/vnd.google-apps.folder', None],
#  ['file2', '1HaHLB19oU_he6LI_6mkQsPr9uH_JK_oz', '0AAeKIFCqYN07Uk9PVA', 'application/octet-stream', '0']]

フォルダはfileSizeを持たないので、キーがなくてもエラーにならないget()メソッドを使う。

また、共有フォルダのファイルなどはparentsを持たない場合もあるので、リスト内包表記の条件式で判定して除外している。

作成した2次元リストをコンストラクタpd.DataFrame()に渡してDataFrameを生成する。

df = pd.DataFrame(l, columns=['title', 'id', 'parents_id', 'mimeType', 'fileSize'])
print(df)
#      title                                 id  \
# 0   file11  15sy12U3d845pXjLaEWww4VLO9Vs7C4J9   
# 1   file12  1iv9UB1pi65MqfFmqYwBoz3AfZbKr2jLx   
# 2  file111  1bfc7jlDobxuoyZeF8CDyXlN66zl84Ech   
# 3    file1  1z1vv7Zd1k14JebVghq8NlVpLWmFhZybo   
# 4   file21  1o-pFt-9Hrj_z3FmlsQfa8duo_ZfuEh5S   
# 5     dir2  1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9   
# 6  subdir1  1CpPdnFTdN6DWPz7VBM1pV-RMZfcbvzck   
# 7     dir1  1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1   
# 8    file2  1HaHLB19oU_he6LI_6mkQsPr9uH_JK_oz   
# 
#                           parents_id                            mimeType  \
# 0  1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1            application/octet-stream   
# 1  1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1            application/octet-stream   
# 2  1CpPdnFTdN6DWPz7VBM1pV-RMZfcbvzck            application/octet-stream   
# 3                0AAeKIFCqYN07Uk9PVA            application/octet-stream   
# 4  1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9            application/octet-stream   
# 5                0AAeKIFCqYN07Uk9PVA  application/vnd.google-apps.folder   
# 6  1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1  application/vnd.google-apps.folder   
# 7                0AAeKIFCqYN07Uk9PVA  application/vnd.google-apps.folder   
# 8                0AAeKIFCqYN07Uk9PVA            application/octet-stream   
# 
#   fileSize  
# 0        0  
# 1        0  
# 2        0  
# 3        0  
# 4        0  
# 5     None  
# 6     None  
# 7     None  
# 8        0  

DataFrameが生成できれば、CSVやEXCELなどのファイルとして保存するのも簡単。

df.to_csv('dst/use_pandas.csv', index=False)

親フォルダをパス形式(xxx/yyy/zzz)で表す

リストをDataFrameに変換すると様々な処理が簡単に行える。

ここでは親フォルダをパス形式(xxx/yyy/zzz)で表す例を示す。なお、Googleドライブは様々なケースがあるため、以下のコードはすべての状況での動作を保証するものではない。

上のサンプルコードのように、すべてのファイル・フォルダを対象としたリストから、id, parents_id, mimeTypeの情報を含むDataFrameを生成したとする。

まず、mimeTypeを判定し、フォルダのみのDataFrameを取得。

フォルダのみのDataFrameをもとに、フォルダのIDとそのタイトル(フォルダ名)、フォルダのIDとその親フォルダのIDをマッピングした辞書をそれぞれ生成する。

df_folder = df.query('mimeType == "application/vnd.google-apps.folder"')

d_id_title = dict(zip(df_folder['id'], df_folder['title']))
d_id_parents_id = dict(zip(df_folder['id'], df_folder['parents_id']))

pprint.pprint(d_id_title)
# {'1CpPdnFTdN6DWPz7VBM1pV-RMZfcbvzck': 'subdir1',
#  '1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1': 'dir1',
#  '1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9': 'dir2'}

pprint.pprint(d_id_parents_id)
# {'1CpPdnFTdN6DWPz7VBM1pV-RMZfcbvzck': '1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1',
#  '1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1': '0AAeKIFCqYN07Uk9PVA',
#  '1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9': '0AAeKIFCqYN07Uk9PVA'}

ルートフォルダ(マイフォルダ)のIDを取得。親フォルダのIDをルートになるまで遡り、パス形式(xxx/yyy/zzz)の文字列に変換し、フォルダのIDとその文字列をマッピングした辞書を生成する。

def get_parents_path(current_id, root_id, sep='/'):
    l = []
    while current_id != root_id:
        if current_id not in d_id_title.keys():
            return None
        l.append(d_id_title[current_id])
        current_id = d_id_parents_id[current_id]
    return sep.join(reversed(l))

root_id = drive.ListFile({'q': '"root" in parents'}).GetList()[0]['parents'][0]['id']

d_id_path = {i: get_parents_path(i, root_id) for i in df_folder['id']}
d_id_path[root_id] = ''

pprint.pprint(d_id_path)
# {'0AAeKIFCqYN07Uk9PVA': '',
#  '1CpPdnFTdN6DWPz7VBM1pV-RMZfcbvzck': 'dir1/subdir1',
#  '1DX1YFUdbSAeIvuXVLoosAKY2eX27Q4Y1': 'dir1',
#  '1UaNdnbqCKe2ALtZp6hirvCgZl7OVpTI9': 'dir2'}

map()でフォルダのIDをパス形式の文字列に置き換え、新たな列を追加。ソートなどを行う。

df['parents_path'] = df['parents_id'].map(d_id_path)
df_result = df.sort_values('parents_path').reset_index(drop=True)
print(df_result[['title', 'parents_path']])
#      title  parents_path
# 0    file1              
# 1     dir2              
# 2     dir1              
# 3    file2              
# 4   file11          dir1
# 5   file12          dir1
# 6  subdir1          dir1
# 7  file111  dir1/subdir1
# 8   file21          dir2

例ではルート以下すべてのファイル・フォルダをリスト化して処理したが、特定のフォルダ以下のサブフォルダの中身を含むすべてのファイル・フォルダのリストがあれば同様の処理が可能。そのフォルダのIDになるまで遡ればよい。

サブフォルダの中身を含むすべてのファイル・フォルダのリストの取得については以下の記事を参照。

関連カテゴリー

関連記事