はじめに
業務でpython使っているので、試してみる
結果、AI使ったら結構簡単にできた。が意味は分かってない
会社でwebブラウザを使った何かのオペレーションをpythonで自動化する場合、AIがあればなんとかなりそうだなと思うのであった。
しかし、会社ではいろんなセキュリティ要素で障害が多そうではある。
今回GCPが必要になりますが、無課金で行えます。GCP使ってない人は使える状態にしましょう
Google Cloud プロジェクトを作成
Google Cloudにログインし、drive-upload-sample という新しいプロジェクトを作成
なぜ Google Cloud が必要か?
Google Drive は API 経由で操作するしかない
→ ログイン画面を自動化(Selenium 等)するのは Google のセキュリティ(2段階認証・CAPTCHA)でほぼブロックされる。
API を使うには Google が発行する「認証情報」 が必要
→ その認証情報を発行・管理する場所が Google Cloud Console。
つまり Google Cloud を経由しないと、Google公式に認められた「安全な入り口」が手に入らない。
Drive API を有効化
Google Drive API ページ
を開く。
「有効にする」ボタンをクリック。
→ これでこのプロジェクトで Drive API が使えるようになります。
これを使わないのとグーグルドライブにアクセスできない
Google Cloud = API鍵やOAuth認証情報を発行する場所
Drive API = 実際にファイルをアップロードするための窓口
OAuth 同意画面を設定
-
左メニュー「APIとサービス」→「OAuth同意画面」へ。
-
「外部」を選択(社内だけでなく自分用でもOK)。※内部は選択不可だった。ワークスペースがいるらしい
-
アプリ名を入力(例: DriveUploader)。
-
ユーザーサポートメール: 自分の Gmail アドレス。
-
「スコープの追加」で .../auth/drive.file を選択(最小権限でOK)。
-
「保存して次へ」を押して最後まで完了。
役割:「このアプリが Drive にアクセスしてもいいですか?」をユーザーに説明する画面を作る
内容:
アプリ名(例: DriveUploader)
サポート用メール
どの権限(スコープ)を要求するか(例: drive.file)
意味:ユーザーが「このアプリを信じて権限を渡す」ための同意プロセス。
👉 要するに ユーザーに見せる説明書きです。
OAuth クライアントIDを作成
-
左メニュー「APIとサービス」→「認証情報」。
-
「認証情報を作成」→「OAuthクライアントID」。
-
アプリケーションの種類で「デスクトップアプリ」を選択。
-
名前は何でもOK(例: DriveUploaderClient)。
-
作成すると「JSONをダウンロード」できる。
-
このファイルが credentials.json です。ダウンロードするファイルはcredentials.jsonにリネームする
役割:アプリを識別するIDと秘密鍵(credentials.json)を発行すること
内容:
クライアントID / クライアントシークレット
リダイレクトURL(今回なら http://localhost) ※今回はPCから実行するため
意味:Python スクリプトが Google に「私はこのアプリです」と名乗るための鍵。
👉 要するに アプリ本体の身分証明書です。
credentials.jsonとtest.txtをプログラムのあるディレクトリに配置
credentials.jsonは公開NG
依存インストール
pip install google-api-python-client google-auth-oauthlib google-auth-httplib2
google-api-python-client → 実際にDrive APIを叩く「手」
google-auth-oauthlib → 最初のログイン/同意フローをやる「玄関」
google-auth-httplib2 → 認証つきで安全に通信する「配達員」
コード作成
# main.py
# Google Drive に任意ファイルをアップロードする最小スクリプト
#
# 目的:
# - ローカルファイル (デフォルト: test.txt) を Google Drive のマイドライブ直下にアップロードする
# - 初回のみ OAuth2 認証フローでブラウザが開き、Googleアカウントの同意が必要
# - 同意後に token.json が保存され、2回目以降は無人でアップロード可能
#
# 想定ユースケース:
# - 定期的に生成されるレポート/ログを Drive に送る
# - Selenium 等で取得した成果物を自動保存する
#
# 実行例:
# python main.py # デフォルト: test.txt をアップロード
# python main.py --file data.csv # 任意ファイルをアップロード
#
# 注意:
# - credentials.json / token.json は機密情報なので絶対に共有しないこと
# - API スコープは最小権限 (drive.file) にしてあり、安全性を確保
#
# 必要ライブラリ:
# pip install google-api-python-client google-auth-oauthlib google-auth-httplib2
import argparse
import os
import sys
from pathlib import Path
from typing import Optional
# ---- Google Drive API 関連 ----
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
from googleapiclient.errors import HttpError
# ---- Google 認証関連 ----
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# スコープ設定
# drive.file: 「このアプリが作成/開いたファイル」にのみアクセス可能 (最小権限で安全)
SCOPES = ["https://www.googleapis.com/auth/drive.file"]
# ファイル名定義
CREDENTIALS_FILE = "credentials.json" # Google Cloud Console からダウンロードしたOAuthクライアント情報
TOKEN_FILE = "token.json" # 初回認証後に自動生成されるアクセストークン
def get_drive_service() -> "googleapiclient.discovery.Resource":
"""
Google Drive API サービスオブジェクトを返す。
- token.json があればそれを使う
- 期限切れなら refresh_token で更新
- 初回 or エラー時は credentials.json から認証フローを開始
"""
creds: Optional[Credentials] = None
# 既存トークンの読み込み
if os.path.exists(TOKEN_FILE):
creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)
# トークンが無効 or 存在しない場合
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
try:
# 有効期限切れ → refresh_token で再取得
creds.refresh(Request())
except Exception as e:
print(f"[WARN] トークンリフレッシュ失敗。再同意が必要です: {e}")
creds = None
if not creds:
# credentials.json が存在しない場合は即エラー
if not os.path.exists(CREDENTIALS_FILE):
print(f"[ERROR] {CREDENTIALS_FILE} が見つかりません。"
f"Google Cloud で OAuth クライアント(デスクトップ)を作成し、"
f"このファイルを同じフォルダに配置してください。")
sys.exit(1)
# 初回認証フロー開始(ブラウザが開き、同意画面+2FA)
flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_FILE, SCOPES)
creds = flow.run_local_server(port=0)
# 新しいトークンを保存(次回以降は無人で利用可能)
with open(TOKEN_FILE, "w", encoding="utf-8") as token:
token.write(creds.to_json())
# 認証済みクライアントを返す
return build("drive", "v3", credentials=creds)
def upload_file(service, filepath: Path, mime: Optional[str] = None) -> str:
"""
Google Drive にファイルをアップロードする。
- マイドライブ直下に保存 (parents=["root"] で明示指定)
- 成功すると fileId を返す
"""
file_metadata = {
"name": filepath.name,
"parents": ["root"], # マイドライブ直下
}
media = MediaFileUpload(filepath.as_posix(), mimetype=mime, resumable=True)
created = service.files().create(
body=file_metadata,
media_body=media,
fields="id, name"
).execute()
return created["id"]
def guess_mime_from_suffix(path: Path) -> Optional[str]:
"""
簡易 MIME タイプ推定。
- 今回は .txt のみ text/plain と判定
- その他は None として Drive 側に推定を任せる
"""
if path.suffix.lower() == ".txt":
return "text/plain"
return None
def main():
# ---- 引数処理 ----
parser = argparse.ArgumentParser(description="Upload a file to Google Drive (My Drive root).")
parser.add_argument("--file", "-f", default="test.txt",
help="アップロードするファイルのパス (デフォルト: test.txt)")
args = parser.parse_args()
filepath = Path(args.file).resolve()
# ファイル存在確認
if not filepath.exists() or not filepath.is_file():
print(f"[ERROR] アップロード元ファイルが見つかりません: {filepath}")
sys.exit(1)
try:
# Drive API サービス初期化
service = get_drive_service()
# MIME タイプ判定
mime = guess_mime_from_suffix(filepath)
# アップロード実行
file_id = upload_file(service, filepath, mime=mime)
print(f"[OK] アップロード完了: name={filepath.name}, fileId={file_id}")
except HttpError as e:
# Google API 側のエラー
print(f"[ERROR] Google API error: {e}")
sys.exit(2)
except Exception as e:
# その他想定外エラー
print(f"[ERROR] 想定外のエラー: {e}")
sys.exit(3)
if __name__ == "__main__":
main()
実行テスト
python main.py --file test.txt
エラー&修正&再実行

これは Google OAuth の「審査プロセス未完了」エラーですね。
ただし、あなたが自分のアカウントで使うだけなら 審査は不要 で回避できます。
自分のDrive用(個人利用) → 審査不要。テストユーザーに自分を追加すればOK。
他人のDrive用(配布・顧客利用) → 審査必要。Googleに申請し、承認されるまで本番公開できない。
設定を直しました。コードは直してません
アップロード成功しました
credentials.json
中身の意味
client_id
アプリ(今回の Python スクリプト)を識別するための公開ID。
「Googleさん、私はこのアプリです」と名乗るためのもの。
project_id
Google Cloud 上のプロジェクト名。どのプロジェクトで発行された認証情報かを示す。
auth_uri
認証リクエストを投げるURL(Googleのログイン画面)。
token_uri
アクセストークン(短期利用の鍵)をもらうAPIのURL。
auth_provider_x509_cert_url
Googleの認証サーバーの公開鍵を取得するためのURL。署名検証に使う。
client_secret
アプリが Google に「正規のアプリです」と証明する秘密鍵。
(他人に渡してはいけない重要情報)
redirect_uris
認証後に Google が「ログインOK! このコードどうぞ」と結果を返す場所。
今回は http://localhost → ローカルPCのスクリプトに直接戻す。
実行するとtoken.jsonが生成される。
これは「初回ログイン(OAuth認証)完了後に自動生成される、アクセス用の鍵情報」です。
それぞれのフィールドの意味を簡潔に説明します👇
中身の意味
token
今まさに有効な アクセストークン。
有効期限が短く(通常1時間)、このトークンを使って Drive API にアクセスします。
refresh_token
アクセストークンが切れたときに、新しいトークンをもらうための「更新用の鍵」。
これがあるから、2回目以降は再ログイン不要で無人実行できます。
(これが一番重要。なくすと再認証が必要になります)
token_uri
アクセストークンを更新するときに叩く Google のAPIエンドポイント。
client_id / client_secret
最初に作った credentials.json の中身と同じ。
どのアプリが使っているかを示す ID と秘密鍵。
scopes
このトークンで許可されている権限の範囲。
今回は https://www.googleapis.com/auth/drive.file →
「このアプリが作った/開いたファイルに限定して操作可能」という最小権限。
universe_domain
認証を提供している Google のドメイン。通常は googleapis.com。
account
認証したアカウントを表すフィールド(空欄でも問題なし)。
expiry
アクセストークンの有効期限。
この時間を過ぎると token は無効になるが、refresh_token を使って更新されます。
まとめると
token.json は 「Drive にアクセスするための通行証」
token → 今回のチケット(すぐ期限切れになる)
refresh_token → 自動で新しいチケットをもらうための鍵(ずっと大事)
これのおかげで、次回以降はログイン画面を開かずに自動アップロードできる
次は何も操作なくアップロードできた
PS C:\Users\tmori\python-repo> python rensyu1.py --file test2.txt
[OK] アップロード完了: name=test2.txt, fileId=14wVGmi7OtTXpDfZNewyIvjEJxVoT0YAe
PS C:\Users\tmori\python-repo>
思ったより簡単にできた。会社の場合、どうするんだろうねと疑問は沸いた。自分のドライブじゃないから
次に、Yahoo! JAPAN を開いてスクリーンショットを撮り、Google Drive(マイドライブ直下)にアップロードする
コード作成
# yahoo_screenshot_to_drive.py
#
# 処理概要:
# 1. SeleniumでChromeを起動してYahoo! JAPANを開く
# 2. ページロードを待機してスクリーンショットを撮影
# 3. Google Drive APIを使ってマイドライブ直下にアップロード
#
# 前提条件:
# - 同じフォルダに credentials.json (OAuthクライアント: デスクトップアプリ) があること
# - 初回実行時のみブラウザでGoogleログイン→2段階認証(スマホで「はい」) → 権限同意
# - 同意後に token.json が生成され、以降は自動的にDriveにアップロード可能
#
# 実行例:
# python yahoo_screenshot_to_drive.py
# python yahoo_screenshot_to_drive.py --url https://www.yahoo.co.jp/ --width 1920 --height 1080
#
# 必要ライブラリ:
# pip install selenium google-api-python-client google-auth-oauthlib google-auth-httplib2
#
# 注意:
# - 初回実行時はブラウザが自動で開きます
# - credentials.json / token.json は秘密情報なので絶対に共有しないこと
import argparse
import os
from pathlib import Path
from datetime import datetime
from typing import Optional
# ---- Google Drive API関連ライブラリ ----
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
from googleapiclient.errors import HttpError
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# ---- Selenium関連ライブラリ ----
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
# スコープ: drive.file = このアプリが作成/開いたファイルのみにアクセス(最小権限)
SCOPES = ["https://www.googleapis.com/auth/drive.file"]
CREDENTIALS_FILE = "credentials.json"
TOKEN_FILE = "token.json"
def get_drive_service():
"""
Google Drive APIクライアントを返す。
- token.json が存在し有効ならそれを使う
- 無効/期限切れならリフレッシュ or 再同意
- 初回実行時はブラウザを開いて同意フローを実施
"""
creds: Optional[Credentials] = None
# 既存のトークンがあれば読み込み
if os.path.exists(TOKEN_FILE):
creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)
# トークンが無効なら更新 or 再同意
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
try:
creds.refresh(Request()) # 有効期限切れ → refresh_tokenで更新
except Exception:
creds = None
if not creds:
# credentials.json が無ければ終了
if not os.path.exists(CREDENTIALS_FILE):
raise FileNotFoundError(
f"{CREDENTIALS_FILE} が見つかりません。Google Cloud で発行して配置してください。"
)
# 同意フロー開始(ブラウザ起動)
flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_FILE, SCOPES)
creds = flow.run_local_server(port=0)
# 新しいトークンを保存(次回以降は無人で利用可能)
with open(TOKEN_FILE, "w", encoding="utf-8") as f:
f.write(creds.to_json())
return build("drive", "v3", credentials=creds)
def upload_to_drive(service, file_path: Path, mime: Optional[str] = None) -> str:
"""
指定ファイルをGoogle Driveのマイドライブ直下にアップロードする。
:param service: Google Drive API サービスオブジェクト
:param file_path: アップロード対象ファイルのPath
:param mime: MIMEタイプ(例: "image/png")
:return: Google Drive上の fileId
"""
metadata = {"name": file_path.name, "parents": ["root"]}
media = MediaFileUpload(file_path.as_posix(), mimetype=mime, resumable=True)
created = service.files().create(body=metadata, media_body=media, fields="id,name").execute()
return created["id"]
def new_chrome(headless: bool = True, width: int = 1366, height: int = 768):
"""
SeleniumでChromeを起動する。
- Selenium 4.6以降はSelenium Managerがchromedriverを自動解決
- ヘッドレスモードも対応
"""
opts = Options()
if headless:
opts.add_argument("--headless=new") # 新しいheadlessモード
opts.add_argument(f"--window-size={width},{height}")
opts.add_argument("--disable-gpu")
opts.add_argument("--no-sandbox")
opts.add_argument("--disable-dev-shm-usage")
opts.add_argument("--lang=ja-JP")
# 言語設定を日本語に
prefs = {"intl.accept_languages": "ja-JP,ja"}
opts.add_experimental_option("prefs", prefs)
return webdriver.Chrome(options=opts)
def wait_page_loaded(driver, timeout_sec: int = 20):
"""
ページのロード完了(document.readyState == "complete")まで待機する。
"""
WebDriverWait(driver, timeout_sec).until(
lambda d: d.execute_script("return document.readyState") == "complete"
)
def main():
# ---- コマンドライン引数の設定 ----
parser = argparse.ArgumentParser(description="Open Yahoo! JAPAN, take screenshot, upload to Google Drive.")
parser.add_argument("--url", default="https://www.yahoo.co.jp/", help="対象URL (デフォルト: Yahoo! JAPAN)")
parser.add_argument("--width", type=int, default=1366, help="ウィンドウ幅")
parser.add_argument("--height", type=int, default=768, help="ウィンドウ高さ")
parser.add_argument("--outfile", default=None, help="出力PNGパス(省略時は artifacts/screenshots に保存)")
args = parser.parse_args()
# スクショの保存先を決定
out_dir = Path("artifacts/screenshots")
out_dir.mkdir(parents=True, exist_ok=True)
ts = datetime.now().strftime("%Y%m%d-%H%M%S")
out_path = Path(args.outfile) if args.outfile else out_dir / f"yahoo_{ts}.png"
driver = None
try:
# ---- ブラウザ起動&ページ読み込み ----
driver = new_chrome(headless=True, width=args.width, height=args.height)
driver.get(args.url)
wait_page_loaded(driver, timeout_sec=30)
# スクリーンショットを保存
ok = driver.save_screenshot(out_path.as_posix())
if not ok:
raise RuntimeError("スクリーンショット保存に失敗しました。")
print(f"[OK] スクリーンショット保存: {out_path}")
# ---- Google Driveにアップロード ----
service = get_drive_service()
file_id = upload_to_drive(service, out_path, mime="image/png")
print(f"[OK] Google Drive アップロード完了: fileId={file_id}")
except HttpError as e:
print(f"[ERROR] Google API error: {e}")
except Exception as e:
print(f"[ERROR] 想定外エラー: {e}")
finally:
if driver:
driver.quit()
if __name__ == "__main__":
main()
セレニウムインストール、ほかのライブラリも
pip install selenium google-api-python-client google-auth-oauthlib google-auth-httplib2
python -m pip show selenium
PS C:\Users\tmori\python-repo> python -m pip show selenium
Name: selenium
Version: 4.35.0
Summary: Official Python bindings for Selenium WebDriver
Home-page: https://www.selenium.dev
Author:
Author-email:
License: Apache-2.0
Location: C:\Users\tmori\AppData\Local\Programs\Python\Python313\Lib\site-packages
Requires: certifi, trio, trio-websocket, typing_extensions, urllib3, websocket-client
Required-by:
PS C:\Users\tmori\python-repo>
実行。エラーっぽい出力があるが成功した。
PS C:\Users\tmori\python-repo> python yahoo_screenshot_to_drive.py --url https://www.yahoo.co.jp/ --width 1920 --height 1080
DevTools listening on ws://127.0.0.1:55244/devtools/browser/8f4ed5df-ed91-48b8-9d1f-90f51f7bad8c
[17120:7400:1002/211902.184:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returnDevTools listening on ws://127.0.0.1:55244/devtools/browser/8f4ed5df-ed91-48b8-9d1f-90f51f7bad8c
[17120:7400:1002/211902.184:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211902.589:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211902.589:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211902.755:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211902.755:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211902.805:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211903.159:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211903.188:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211929.023:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211902.805:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211903.159:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211903.188:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211929.023:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211903.159:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211903.188:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211929.023:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; return[17120:7400:1002/211903.159:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211903.188:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211929.023:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; return[17120:7400:1002/211903.188:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211929.023:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211929.023:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; return[17120:7400:1002/211929.023:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211929.421:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
ed -1, SSL error code 1, net_error -107
[17120:7400:1002/211929.421:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
[17120:7400:1002/211929.421:ERROR:net\socket\ssl_client_socket_impl.cc:902] handshake failed; returned -1, SSL error code 1, net_error -107
ed -1, SSL error code 1, net_error -107
[OK] スクリーンショット保存: artifacts\screenshots\yahoo_20251002-211858.png
[OK] スクリーンショット保存: artifacts\screenshots\yahoo_20251002-211858.png
[14036:23832:1002/211930.437:ERROR:components\device_event_log\device_event_log_impl.cc:198] [21:19:30.437] USB: usb_service_win.cc:105 SetupDiGetDeviceProperty({{A45C254E-DF1C-4EFD-8020-67D146A850E0}30.437] USB: usb_service_win.cc:105 SetupDiGetDeviceProperty({{A45C254E-DF1C-4EFD-8020-67D146A850E0}, 6}) failed: 隕∫エ縺瑚ヲ九▽縺九j縺セ縺帙s縲・(0x490)
, 6}) failed: 隕∫エ縺瑚ヲ九▽縺九j縺セ縺帙s縲・(0x490)
[14036:21856:1002/211931.246:ERROR:google_apis\gcm\engine\registration_request.cc:291] Registration [14036:21856:1002/211931.246:ERROR:google_apis\gcm\engine\registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT
[OK] Google Drive アップロード完了: fileId=1ofJkGkIP7HQrsSJON527NXaTIarrRfKT
PS C:\Users\tmori\python-repo>
途中のエラーっぽい出力の意味
- handshake failed; returned -1, SSL error code 1, net_error -107
これは Chrome が裏で開く DevTools の通信でSSLハンドシェイクが一時失敗したログです。
よくある原因:
サイトが多数のサブリソースにアクセスしていて、その中の1つがタイムアウト/SSL不一致
ローカル環境で証明書の検証に時間がかかる
実際にはページロードが続行できて、スクショも成功してるので「無害な警告」に近いです。
token.jsonの役割がわからないんだけど、credential.jsonだけで完結しないのか?なぜ両方必要なのか?
まず整理:credentials.json と token.json の違い
- credentials.json
Google Cloud Console からダウンロードするファイル
中身は「クライアントID」と「クライアントシークレット」などの固定情報
アプリが Google に対して
「私はこのアプリ(DriveUploader)です」
と名乗るための 身分証明書
ただしこれだけでは「誰がログインしているか(ユーザー情報)」は分からない
👉 あくまでアプリ側の身分証明だけ
- token.json
初回認証フローを通したあとに自動生成されるファイル
中身は「ユーザー本人がDriveへのアクセスを許可した証明(アクセストークン+リフレッシュトークン)」
アプリが Google に対して
「このユーザーはすでにログイン済みで、Driveアクセスを許可しています」
と伝えるための鍵
これがあるおかげで、2回目以降はブラウザログインやスマホで「はい」を押さなくても済む
👉 ユーザー本人のログイン情報を保持するキャッシュ
なぜ両方必要なのか?
credentials.json …「アプリの身元」を示す(固定)
token.json …「ユーザーの認可済み状態」を保持する(変動)
例えるなら:
credentials.json = 「社員証(会社発行のIDカード)」
token.json = 「その社員が入館ゲートを通るときにもらった通行証(期限つきパス)」
社員証だけあっても、入館するたびに受付で「本人確認+書類提出」が必要になります。
でも一度通行証をもらえば、次からはゲートをスッと通れる。
その「通行証」を保存しているのが token.json です。
もし token.json を使わなかったら?
スクリプトを実行するたびに
ブラウザが開く
Googleログイン
2FAでスマホ「はい」
権限許可
を毎回やる羽目になります。
自動化したいのに、毎回手動操作が必要になってしまう → 自動化の意味がなくなる





