Google API Python Clientをインストール
PyDrive2の前にまず、Google API Python ClientをRaspberry Piへインストールします(執筆当時最新2.36.0)。
1 2 3 4 5 6 7 |
$ pip install -U pip $ pip install --upgrade google-api-python-client Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple Collecting google-api-python-client Downloading https://files.pythonhosted.org/packages/fa/4a/0e67e88b1b884b822d805cba0b1dc3a64e4359b655a6db16f61c6f983783/google_api_python_client-2.36.0-py2.py3-none-any.whl (8.0MB) 100% |████████████████████████████████| 8.0MB 3.9kB/s Could not install packages due to an EnvironmentError: [Errno 28] No space left on device |
pip のこのエラーは以前、作業用ディレクトリ/tmpをtmpfs化した際に16MBしか割当てていなかったため、 pip がデフォルトで作業場所として使用する /tmp が容量不足となったものです。
1 2 3 4 |
$ df -h /tmp /var/tmp Filesystem Size Used Avail Use% Mounted on tmpfs 16M 8.0K 16M 1% /tmp tmpfs 16M 0 16M 0% /var/tmp |
pip はダウンロード時のキャッシュフォルダと、その後の展開ビルド作業用フォルダを使用するので、オプションでキャッシュは無効の上、 $HOME 下に一次的な作業フォルダを用意してビルド場所に指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
$ pip install --upgrade --no-cache-dir --build $HOME/tmp/ google-api-python-client Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple Collecting google-api-python-client Downloading https://files.pythonhosted.org/packages/fa/4a/0e67e88b1b884b822d805cba0b1dc3a64e4359b655a6db16f61c6f983783/google_api_python_client-2.36.0-py2.py3-none-any.whl (8.0MB) 100% |████████████████████████████████| 8.0MB 536kB/s Collecting google-api-core<3.0.0dev,>=1.21.0 (from google-api-python-client) Downloading https://files.pythonhosted.org/packages/52/3f/0599f4eacbddb0dff09c9e039658d6c39e429ec78e32a9e2781ff89e4e65/google_api_core-2.4.0-py2.py3-none-any.whl (111kB) 100% |████████████████████████████████| 112kB 661kB/s Collecting httplib2<1dev,>=0.15.0 (from google-api-python-client) Downloading https://files.pythonhosted.org/packages/b3/20/511741a05f8a59dc98f5ad9962efdc4f7bc1126329234e18b13f0cb3ddc4/httplib2-0.20.2-py3-none-any.whl (96kB) 100% |████████████████████████████████| 102kB 506kB/s Collecting uritemplate<5,>=3.0.1 (from google-api-python-client) Downloading https://files.pythonhosted.org/packages/81/c0/7461b49cd25aeece13766f02ee576d1db528f1c37ce69aee300e075b485b/uritemplate-4.1.1-py2.py3-none-any.whl Collecting google-auth-httplib2>=0.1.0 (from google-api-python-client) Downloading https://files.pythonhosted.org/packages/ba/db/721e2f3f32339080153995d16e46edc3a7657251f167ddcb9327e632783b/google_auth_httplib2-0.1.0-py2.py3-none-any.whl Collecting google-auth<3.0.0dev,>=1.16.0 (from google-api-python-client) Downloading https://files.pythonhosted.org/packages/ed/c8/24b2007dacaa0d4255df62485a92363288eec962deb2dafcc046a5b17aae/google_auth-2.5.0-py2.py3-none-any.whl (157kB) 100% |████████████████████████████████| 163kB 756kB/s Collecting googleapis-common-protos<2.0dev,>=1.52.0 (from google-api-core<3.0.0dev,>=1.21.0->google-api-python-client) Downloading https://files.pythonhosted.org/packages/5d/d2/38c15431197c137358968b5d2e17184832a4447d3580b03617824af71020/googleapis_common_protos-1.54.0-py2.py3-none-any.whl (207kB) 100% |████████████████████████████████| 215kB 752kB/s Requirement already satisfied, skipping upgrade: requests<3.0.0dev,>=2.18.0 in /usr/lib/python3/dist-packages (from google-api-core<3.0.0dev,>=1.21.0->google-api-python-client) (2.21.0) Collecting protobuf>=3.12.0 (from google-api-core<3.0.0dev,>=1.21.0->google-api-python-client) Downloading https://files.pythonhosted.org/packages/dc/a9/fdc31f2c7917d5a1e18f4ca2f955706dfdcaaba8fb0baa48becfc0b68f22/protobuf-3.19.3-py2.py3-none-any.whl (162kB) 100% |████████████████████████████████| 163kB 546kB/s Requirement already satisfied, skipping upgrade: setuptools>=40.3.0 in /usr/lib/python3/dist-packages (from google-api-core<3.0.0dev,>=1.21.0->google-api-python-client) (40.8.0) Collecting pyparsing!=3.0.0,!=3.0.1,!=3.0.2,!=3.0.3,<4,>=2.4.2; python_version > "3.0" (from httplib2<1dev,>=0.15.0->google-api-python-client) Downloading https://files.pythonhosted.org/packages/80/c1/23fd82ad3121656b585351aba6c19761926bb0db2ebed9e4ff09a43a3fcc/pyparsing-3.0.7-py3-none-any.whl (98kB) 100% |████████████████████████████████| 102kB 713kB/s Requirement already satisfied, skipping upgrade: six in /usr/lib/python3/dist-packages (from google-auth-httplib2>=0.1.0->google-api-python-client) (1.12.0) Collecting cachetools<6.0,>=2.0.0 (from google-auth<3.0.0dev,>=1.16.0->google-api-python-client) Downloading https://files.pythonhosted.org/packages/19/99/ace1769546388976b45e93445bb04c6df95e96363f03fbb56f916da5ebde/cachetools-5.0.0-py3-none-any.whl Collecting rsa<5,>=3.1.4; python_version >= "3.6" (from google-auth<3.0.0dev,>=1.16.0->google-api-python-client) Downloading https://files.pythonhosted.org/packages/30/ab/8fd9e88e6fa5ec41afca995938bbefb72195278e0cfc5bd76a4f29b23fb2/rsa-4.8-py3-none-any.whl Collecting pyasn1-modules>=0.2.1 (from google-auth<3.0.0dev,>=1.16.0->google-api-python-client) Downloading https://files.pythonhosted.org/packages/95/de/214830a981892a3e286c3794f41ae67a4495df1108c3da8a9f62159b9a9d/pyasn1_modules-0.2.8-py2.py3-none-any.whl (155kB) 100% |████████████████████████████████| 163kB 533kB/s Collecting pyasn1>=0.1.3 (from rsa<5,>=3.1.4; python_version >= "3.6"->google-auth<3.0.0dev,>=1.16.0->google-api-python-client) Downloading https://files.pythonhosted.org/packages/62/1e/a94a8d635fa3ce4cfc7f506003548d0a2447ae76fd5ca53932970fe3053f/pyasn1-0.4.8-py2.py3-none-any.whl (77kB) 100% |████████████████████████████████| 81kB 705kB/s Installing collected packages: protobuf, googleapis-common-protos, cachetools, pyasn1, rsa, pyasn1-modules, google-auth, google-api-core, pyparsing, httplib2, uritemplate, google-auth-httplib2, google-api-python-client The scripts pyrsa-decrypt, pyrsa-encrypt, pyrsa-keygen, pyrsa-priv2pub, pyrsa-sign and pyrsa-verify are installed in '/home/pi/.local/bin' which is not on PATH. Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. Successfully installed cachetools-5.0.0 google-api-core-2.4.0 google-api-python-client-2.36.0 google-auth-2.5.0 google-auth-httplib2-0.1.0 googleapis-common-protos-1.54.0 httplib2-0.20.2 protobuf-3.19.3 pyasn1-0.4.8 pyasn1-modules-0.2.8 pyparsing-3.0.7 rsa-4.8 uritemplate-4.1.1 |
Google API Python Clientは8MB程度なものの、依存するライブラリを含め、ビルドに使われた容量は67MBもありました。
1 2 |
$ du -shc ~/tmp 67M ~/tmp |
PyDrive2をインストール
冒頭で述べた通り、PyDrive2も pip でインストールすることが出来るのですが、前述同様、依存関係含めると大きくなりそうなので、ビルド場所を指定してのインストールとしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
$ pip install --upgrade --no-cache-dir --build $HOME/tmp/ PyDrive2 Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple Collecting PyDrive2 Downloading https://files.pythonhosted.org/packages/7d/6a/28b555d61758098edd4ebc9bd1e5b9281bba27c67c97fec6d180166ba9dd/PyDrive2-1.10.0-py3-none-any.whl Requirement already satisfied, skipping upgrade: google-api-python-client>=1.12.5 in ./.local/lib/python3.7/site-packages (from PyDrive2) (2.36.0) Collecting oauth2client>=4.0.0 (from PyDrive2) Downloading https://files.pythonhosted.org/packages/95/a9/4f25a14d23f0786b64875b91784607c2277eff25d48f915e39ff0cff505a/oauth2client-4.1.3-py2.py3-none-any.whl (98kB) 100% |████████████████████████████████| 102kB 679kB/s Collecting six>=1.13.0 (from PyDrive2) Downloading https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl Collecting PyYAML>=3.0 (from PyDrive2) Downloading https://www.piwheels.org/simple/pyyaml/PyYAML-6.0-cp37-cp37m-linux_armv6l.whl (45kB) 100% |████████████████████████████████| 51kB 128kB/s Collecting pyOpenSSL>=19.1.0 (from PyDrive2) Downloading https://files.pythonhosted.org/packages/85/3a/fe3c98435856a1ed798977981f3da82d2685cf9df97e4d9546340d2b83db/pyOpenSSL-21.0.0-py2.py3-none-any.whl (55kB) 100% |████████████████████████████████| 61kB 703kB/s Requirement already satisfied, skipping upgrade: google-api-core<3.0.0dev,>=1.21.0 in ./.local/lib/python3.7/site-packages (from google-api-python-client>=1.12.5->PyDrive2) (2.4.0) Requirement already satisfied, skipping upgrade: httplib2<1dev,>=0.15.0 in ./.local/lib/python3.7/site-packages (from google-api-python-client>=1.12.5->PyDrive2) (0.20.2) Requirement already satisfied, skipping upgrade: google-auth-httplib2>=0.1.0 in ./.local/lib/python3.7/site-packages (from google-api-python-client>=1.12.5->PyDrive2) (0.1.0) Requirement already satisfied, skipping upgrade: google-auth<3.0.0dev,>=1.16.0 in ./.local/lib/python3.7/site-packages (from google-api-python-client>=1.12.5->PyDrive2) (2.5.0) Requirement already satisfied, skipping upgrade: uritemplate<5,>=3.0.1 in ./.local/lib/python3.7/site-packages (from google-api-python-client>=1.12.5->PyDrive2) (4.1.1) Requirement already satisfied, skipping upgrade: pyasn1>=0.1.7 in ./.local/lib/python3.7/site-packages (from oauth2client>=4.0.0->PyDrive2) (0.4.8) Requirement already satisfied, skipping upgrade: pyasn1-modules>=0.0.5 in ./.local/lib/python3.7/site-packages (from oauth2client>=4.0.0->PyDrive2) (0.2.8) Requirement already satisfied, skipping upgrade: rsa>=3.1.4 in ./.local/lib/python3.7/site-packages (from oauth2client>=4.0.0->PyDrive2) (4.8) Collecting cryptography>=3.3 (from pyOpenSSL>=19.1.0->PyDrive2) Downloading https://www.piwheels.org/simple/cryptography/cryptography-36.0.1-cp37-cp37m-linux_armv6l.whl (1.9MB) 100% |████████████████████████████████| 1.9MB 740kB/s Requirement already satisfied, skipping upgrade: requests<3.0.0dev,>=2.18.0 in /usr/lib/python3/dist-packages (from google-api-core<3.0.0dev,>=1.21.0->google-api-python-client>=1.12.5->PyDrive2) (2.21.0) Requirement already satisfied, skipping upgrade: protobuf>=3.12.0 in ./.local/lib/python3.7/site-packages (from google-api-core<3.0.0dev,>=1.21.0->google-api-python-client>=1.12.5->PyDrive2) (3.19.3) Requirement already satisfied, skipping upgrade: googleapis-common-protos<2.0dev,>=1.52.0 in ./.local/lib/python3.7/site-packages (from google-api-core<3.0.0dev,>=1.21.0->google-api-python-client>=1.12.5->PyDrive2) (1.54.0) Requirement already satisfied, skipping upgrade: setuptools>=40.3.0 in /usr/lib/python3/dist-packages (from google-api-core<3.0.0dev,>=1.21.0->google-api-python-client>=1.12.5->PyDrive2) (40.8.0) Requirement already satisfied, skipping upgrade: pyparsing!=3.0.0,!=3.0.1,!=3.0.2,!=3.0.3,<4,>=2.4.2; python_version > "3.0" in ./.local/lib/python3.7/site-packages (from httplib2<1dev,>=0.15.0->google-api-python-client>=1.12.5->PyDrive2) (3.0.7) Requirement already satisfied, skipping upgrade: cachetools<6.0,>=2.0.0 in ./.local/lib/python3.7/site-packages (from google-auth<3.0.0dev,>=1.16.0->google-api-python-client>=1.12.5->PyDrive2) (5.0.0) Collecting cffi>=1.12 (from cryptography>=3.3->pyOpenSSL>=19.1.0->PyDrive2) Downloading https://www.piwheels.org/simple/cffi/cffi-1.15.0-cp37-cp37m-linux_armv6l.whl (346kB) 100% |████████████████████████████████| 348kB 745kB/s Collecting pycparser (from cffi>=1.12->cryptography>=3.3->pyOpenSSL>=19.1.0->PyDrive2) Downloading https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl (118kB) 100% |████████████████████████████████| 122kB 774kB/s Installing collected packages: six, oauth2client, PyYAML, pycparser, cffi, cryptography, pyOpenSSL, PyDrive2 Successfully installed PyDrive2-1.10.0 PyYAML-6.0 cffi-1.15.0 cryptography-36.0.1 oauth2client-4.1.3 pyOpenSSL-21.0.0 pycparser-2.21 six-1.16.0 |
PyDrive2でDrive APIの疎通確立
図11でダウンロードした client_secrets.json をRaspberry Piへコピーした後、PyDrive2公式ページの「OAuth made easy」項に従ってGoogleドライブAPIとの疎通確立を目指します。まず、次の内容を入れた settings.yaml と言うテキストファイルを用意。
1 2 3 4 5 6 7 8 9 10 11 12 |
client_config_backend: file client_config_file: client_secrets.json save_credentials: True save_credentials_backend: file save_credentials_file: credentials.json get_refresh_token: True oauth_scope: - https://www.googleapis.com/auth/drive.file - https://www.googleapis.com/auth/drive.install |
このファイルがOAuth2の挙動を記した設定ファイルで、まず冒頭の client_config_backend が認証情報の記述方法を指定しています。上記の例ではダウンロードした client_secrets.json ファイルを参照するようにしていますが、 settings.yaml へ直接認証情報を記述したい場合は、冒頭を次のように変更します。
1 2 3 4 |
client_config_backend: settings client_config: client_id: ########################### client_secret: ############################### |
続く save_credentials 項は認証通過後の資格情報の保存方法に関する項目で、 credentials.json というファイルで保存すると共に、続く get_refresh_token 項を True とすることで、次回以降の認証作業をパスするようになります。
そして設定ファイルの最下段にある oauth_scope 項目は、付与する権限に関するもので、今回の命題である「Googleドライブへのファイルのアップロード(新規と既存上書き)」であれば上記の権限で十分です。もし、さらに色々な操作の試行において、エラーに遭遇するようであれば、当該項を以下のように記述することでドライブAPIへのフル権限を付与して、問題点の切り分けをしてみると、良いかもしれません。
1 2 |
oauth_scope: - https://www.googleapis.com/auth/drive |
下ごしらえが済んだら、認証のためのPythonスクリプトを実行するのですが、公式ページにある次のスクリプトはブラウザでの認証ウィザードの後、自身の8090ポートへ確認コードを自動的に投げるため、GUIのあるデバイス環境を前提としています。
1 2 3 4 5 6 |
from pydrive2.auth import GoogleAuth from pydrive2.drive import GoogleDrive gauth = GoogleAuth() gauth.LocalWebserverAuth() drive = GoogleDrive(gauth) |
ヘッドレスでSSH越しに作業しているような場合は次のようにすることで、CLI上で示された認証リンクを別途GUIのあるデバイスで開いて認証を進め、最後の確認コードをCLIへペーストして認証を通過させることができます。
1 2 3 4 5 6 |
from pydrive2.auth import GoogleAuth from pydrive2.drive import GoogleDrive gauth = GoogleAuth() gauth.CommandLineAuth() drive = GoogleDrive(gauth) |
実際にPythonコンソールから実行してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ python Python 3.7.3 (default, Jan 22 2021, 20:04:44) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from pydrive2.auth import GoogleAuth >>> from pydrive2.drive import GoogleDrive >>> gauth = GoogleAuth() >>> gauth.CommandLineAuth() ~/.local/lib/python3.7/site-packages/oauth2client/_helpers.py:255: UserWarning: Cannot access credentials.json: No such file or directory warnings.warn(_MISSING_FILE_MESSAGE.format(filename)) Go to the following link in your browser: https://accounts.google.com/o/oauth2/auth?client_id=#################################... Enter verification code: ###################################################### Authentication successful. >>> drive = GoogleDrive(gauth) >>> |
Ctrl-D でPythonコンソールを抜けると、カレントディレクトリには credentials.json ファイルが生成されおり、この状態でもう一度同じスクリプトを実行しても、今度は認証を求められることはありませんでした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ ls -l -rw-r--r-- 1 pi pi 429 Feb 7 23:42 client_secrets.json -rw------- 1 pi pi 1381 Feb 7 23:49 credentials.json -rw-r--r-- 1 pi pi 300 Feb 7 23:42 settings.yaml $ python Python 3.7.3 (default, Jan 22 2021, 20:04:44) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from pydrive2.auth import GoogleAuth >>> from pydrive2.drive import GoogleDrive >>> gauth = GoogleAuth() >>> gauth.CommandLineAuth() >>> drive = GoogleDrive(gauth) >>> |
PyDrive2でのファイルアップロード(新規・既存上書き)
Googleドライブ上のとあるサブフォルダ内へ画像ファイルをするPythonスクリプトは以下の通りです。アップロードの際、既に同名のファイルが存在するか調べ、存在しない場合は新規ファイルとして、存在する場合はそこへ上書きという形で書き込む仕様です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
from pydrive2.auth import GoogleAuth from pydrive2.drive import GoogleDrive gauth = GoogleAuth() gauth.CommandLineAuth() drive = GoogleDrive(gauth) # https://drive.google.com/drive/u/0/folders/################################# folder_id = '#################################' file_path = '/tmp/cam.jpg' ## Check Whether File Exists qstr = "title = \"cam.jpg\" and \"" + folder_id + "\" in parents and trashed=false" files = drive.ListFile({'q': qstr}).GetList() if len(files) > 0: # Overwrite file = files[0] print('File Exists on Drive :\t', file['title'], ' (', file['id'], ')') else: # Create New print('Create New File') file = drive.CreateFile({'title': 'cam.jpg', 'mimeType': 'image/jpeg', 'parents': [{'id': folder_id}]}) ## Upload file.SetContentFile(file_path) file.Upload() |
ブラウザからGoogleドライブを確認してみると、確かに画像ファイルがアップロードされていました。
こうして出来上がった仕組みを使い、Raspberry PiのCSIインターフェイスに繋いだカメラで静止画を撮影、Googleドライブへ定期的にアップロードする定点カメラシステムを次回まとめてみるつもりです。
余談ですが今回の作業において調べものをしている時に、rcloneというツールをよく目にしました。様々なオンラインストレージ相手のファイル操作が出来るこのツールでは、GoogleドライブもサポートされているのでPythonに依らない環境で実現したい場合は、このツールを使ってみるのも良いと思います。