Google Drive で 全公開されているドキュメントを一覧化する

##背景
社内で使用されている、Google Drive のドキュメントで、共有設定が「リンクを知っている全員が編集可」になっているファイルが無いか確認する必要がありました。

GSuiteの管理コンソールから「レポート」->「セキュリティ」で、外部とリンク共有中のファイルを下記方法で確認できます。
しかし、レポートの内容では「誰が」「外部リンク中のファイルを何個もっているか」しか表示されません。

Google API を利用して取得したファイルをスプレッドシートにリストする方法を記載します。
[「Admin SDK」を有効にする方法はこちらを参照してください。](https://qiita.com/naoaki_kaito/items/958ecbe3217ba400ffcf)

- 有効にするAPI
  - Google Drive API
  - Google Sheets API


##コードの仕様
- 誰でもアクセスできるファイルをスプレッドシートにリスト保存する
    - 画像や動画は対象に含めない
    - 最終更新日から100日を超えているものは、共有範囲をドメイン内に設定する
- 何かしらの理由で権限を取得できなかった場合は、別リストに保存する

##実際のコード
```python:list.py
# -*- coding: utf-8 -*-
from __future__ import print_function
import httplib2
import sys, os, time
from datetime import *
from apiclient import discovery
from apiclient import errors
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage

try:
    import argparse
    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None

CLIENT_SECRET_FILE = 'hoge.json'
CREDENTIAL_FILE = 'fuga.json'
APPLICATION_NAME = 'Drive Get Public Files'
SCOPES = 'https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/spreadsheets'
SPREADSHEET_ID = '保存するスプレッドシートのID'

"""
有効な証明書をストレージから取得
"""
def get_credentials():
	#認証情報を格納するディレクトリの作成
	home_dir = os.path.expanduser('~')
	credential_dir = os.path.join(home_dir, '.credentials')
	if not os.path.exists(credential_dir):
		os.makedirs(credential_dir)

	#OSにパスを通す
	credential_path = os.path.join(credential_dir, CREDENTIAL_FILE)

	store = Storage(credential_path)
	credentials = store.get()
	if not credentials or credentials.invalid:
		#認証処理を行うクラスのインスタンスを生成
		flow = client.flow_from_clientsecrets(
			os.path.join(home_dir, '.credentials', 'client_secrets', CLIENT_SECRET_FILE), SCOPES)
		flow.user_agent = APPLICATION_NAME
		if flags:
			credentials = tools.run_flow(flow, store, flags)
		else: # Python 2.6　互換用処理
			credentials = tools.run(flow, store)
		print('Storing credentials to ' + credential_path)
	return credentials

def main():
	credentials = get_credentials()
	http = credentials.authorize(httplib2.Http())
	discovery_url = ('https://sheets.googleapis.com/$discovery/rest?' 'version=v4')
	drive_service = discovery.build('drive', 'v3', http=http)

	# スプレットシートサービスオブジェクトのビルド
	ss_service = discovery.build('sheets', 'v4', http=http, discoveryServiceUrl=discovery_url)

	page_token = None
	public_permissions = []
	unknown_permissions = []

	putblic_last_row = 2
	unknown_last_row = 2

	now_datetime = datetime.now()

	while True:
		response = drive_service.files().list(
				q="mimeType!='application/vnd.google-apps.folder' and mimeType!='image/jpeg' and mimeType!='video/quicktime' ",
				spaces='drive', 
				fields='nextPageToken, files(id, name, webViewLink, modifiedTime)',
				pageToken=page_token
			).execute()

		for file in response.get('files', []):
			try:
				file_id = file.get('id', None)
				memo = '' #備考

				# ファイルの権限を取得
				permissions = drive_service.permissions().list(fileId=file_id).execute()
				permission_items = permissions.get('permissions', [])

				# ドキュメントに設定されている権限リストをループする
				for item in permission_items:
					permission_id = item.get('id', '')
					permission_type = item.get('type', '')
					# 誰でもアクセスできる場合、配列に保存
					if (permission_id == 'anyoneWithLink'):

						# 最終更新日が3ヶ月以上前の場合は、共有設定を変更
						last_update_str = file.get('modifiedTime', None)
						last_update_datetime = datetime.strptime(last_update_str[:-1], '%Y-%m-%dT%H:%M:%S.%f')
						diff_hour = (now_datetime - last_update_datetime).total_seconds() / (3600 * 24)

						if(diff_hour > 100):
							print('100日より前です')

							# 誰でもアクセスできる権限を削除
							drive_service.permissions().delete(fileId=file_id, permissionId='anyoneWithLink').execute()

							# ドメインメンバーがアクセスできる権限を追加
							batch = drive_service.new_batch_http_request(callback=callback)
							batch.add(drive_service.permissions().create(
									fileId=file_id,
									body={
										'role': 'writer',
										'type': 'domain',
										'allowFileDiscovery': False,
										'domain': 'aaaaa.jp',
										'kind': 'drive#permission'
									},
									fields='id'
							))
							batch.execute()
							memo = '100日経過したので共有設定しなおしました'

						#　配列に保存
						public_permissions.append([file.get('webViewLink', None), file.get('name', None), memo])
						print('#', end='', flush=True)

				# 10個ごとにシートに反映する
				if((len(public_permissions) > 0) and (len(public_permissions) % 10 == 0)):
					# ワークシート情報を設定
					range_name = 'file list' + '!A' + str(putblic_last_row)
					body = {'values': public_permissions}

					print('シートに反映')
					result = ss_service.spreadsheets().values().update(
						spreadsheetId=SPREADSHEET_ID, 
						range=range_name, valueInputOption='USER_ENTERED', body=body).execute()

					public_permissions = []
					putblic_last_row += 10

			# 使用中もしくは読み込めずにエラーになった場合
			except:
				unknown_permissions.append([file.get('webViewLink', None), file.get('name', None)])
				print('%', end='', flush=True)

				# 10個ごとにシートに反映する
				if((len(unknown_permissions) > 0) and (len(unknown_permissions) % 10 == 0)):
					range_name = 'file list' + '!E' + str(unknown_last_row)
					body = {'values': unknown_permissions}

					print('シートに反映')
					result = ss_service.spreadsheets().values().update(
						spreadsheetId=SPREADSHEET_ID, 
						range=range_name, valueInputOption='USER_ENTERED', body=body).execute()

					unknown_permissions = []
					unknown_last_row += 10

				continue

		page_token = response.get('nextPageToken', None)
		if page_token is None:
			break;


def callback(request_id, response, exception):
	if exception:
		# Handle error
		print(exception)
	else:
		print("Permission Id: %s" % response.get('id'))

if __name__ == '__main__':
	main()
```