Google drive 公開設定

Google drive 公開設定

from googleapiclient.discovery import build
from google.oauth2 import service_account

# 認証情報のセットアップ(Google Drive API 用のサービスアカウント)
SCOPES = ["https://www.googleapis.com/auth/drive.metadata.readonly"]
SERVICE_ACCOUNT_FILE = "service_account.json"

creds = service_account.Credentials.from_service_account_file(
    SERVICE_ACCOUNT_FILE, scopes=SCOPES
)
service = build("drive", "v3", credentials=creds)

# 共有されているフォルダを検索
query = "mimeType='application/vnd.google-apps.folder' and sharedWithMe"
results = service.files().list(q=query, fields="files(id, name, owners, webViewLink)").execute()
folders = results.get("files", [])

if not folders:
    print("公開されているフォルダはありません。")
else:
    for folder in folders:
        print(f"フォルダ名: {folder['name']}, URL: {folder['webViewLink']}, 所有者: {folder['owners'][0]['emailAddress']}")

の結果からいくつか気になったのでGPTで確認

となったが
✅ Google Drive フォルダの webViewLink が表示されるなら公開URLになっているのか?
結論:まだ「完全な公開URL」にはなっていません! 🚨
* webViewLink は 「Google Drive 上でフォルダを閲覧できるURL」 であり、
誰でも直接アクセスできる公開URLではない 可能性があります。
* LINE Bot で画像を表示するには「画像自体のURL」が必要 であり、フォルダの webViewLink ではなく、画像ファイルごとのURLを取得する必要がある。

とのこと

🚀 1. webViewLink とは?
* webViewLink のURL(例: https://drive.google.com/drive/folders/…)は、Google Drive 上のフォルダを開くためのもの。
* これは「フォルダの中身をブラウザで表示する」ためのURLであり、LINE API で画像を表示するための直接URLではない。
✅ 公開されているフォルダなら、自分のアカウントでアクセス可能 ❌ ただし、画像を埋め込むには、個々の画像の公開URLが必要

本当にフォルダ内の画像が公開されているか確認
フォルダが「共有」されているだけでは、フォルダ内のファイルが「完全公開」されているとは限りません。
各画像の permissions を確認することで、画像が一般公開されているかどうかをチェック

python folder_access.py 

from googleapiclient.discovery import build
from google.oauth2 import service_account

# Google Drive API 認証設定
SCOPES = ["https://www.googleapis.com/auth/drive"]
SERVICE_ACCOUNT_FILE = "service_account.json"

creds = service_account.Credentials.from_service_account_file(
    SERVICE_ACCOUNT_FILE, scopes=SCOPES
)
drive_service = build("drive", "v3", credentials=creds)

# 共有フォルダ ID
FOLDER_ID = ""

def check_image_permissions():
    """フォルダ内の画像が公開されているかチェック"""
    query = f"'{FOLDER_ID}' in parents and mimeType contains 'image/' and trashed=false"
    results = drive_service.files().list(q=query, fields="files(id, name, permissions, webViewLink)").execute()
    files = results.get("files", [])

    if not files:
        print("フォルダ内に画像がありません。")
        return

    for file in files:
        file_id = file["id"]
        file_name = file["name"]
        webViewLink = file.get("webViewLink", "なし")

        # ファイルの公開設定を確認
        permissions = drive_service.permissions().list(fileId=file_id).execute()
        public_access = any(p["type"] == "anyone" for p in permissions.get("permissions", []))

        if public_access:
            print(f"✅ 画像: {file_name} は公開されています。URL: {webViewLink}")
        else:
            print(f"❌ 画像: {file_name} は非公開です。URL: {webViewLink} (アクセス不可)")

# 画像の公開設定を確認
check_image_permissions()

結果は

❌ 画像: PXL_20240617_182349485.jpg は非公開です。URL: https://drive.google.com/file/ (アクセス不可)

となるので
完全公開になっていないため画像が取得できていなかった

✅ 問題点
LINE の originalContentUrl / previewImageUrl に Google Drive の
「uc?id=…」形式のURL を指定しているため、画像が正しく表示されない可能性がある。
Google Drive の 「uc?id=…」形式のURLは、一部の環境では直接開けないことがある(プレビュー画面になってしまう)。

このためコードの変更

def get_drive_image_url(file_id):
    return f"https://drive.google.com/uc?id={file_id}"

から
修正後(公開URLを取得するコードに変更)

def get_drive_image_url(file_id):
    """Google Drive のファイルIDを公開URLに変換する"""
    try:
        # ファイルのアクセス権限を「公開」に設定
        drive_service.permissions().create(
            fileId=file_id,
            body={"role": "reader", "type": "anyone"},
        ).execute()

        # 画像のダウンロードURLを取得
        return f"https://drive.google.com/thumbnail?id={file_id}&sz=w1000"
    except Exception as e:
        print(f"Error making image public: {e}")
        return None

これで全体コードは

import requests
import random
import time
from googleapiclient.discovery import build
from google.oauth2 import service_account

# ① Google Drive API の設定
SCOPES = ["https://www.googleapis.com/auth/drive"]
SERVICE_ACCOUNT_FILE = "service_account.json"

creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
drive_service = build("drive", "v3", credentials=creds)

# ② 送信先の LINE API 設定
LINE_PUSH_URL = "https://api.line.me/v2/bot/message/push"
LINE_CHANNEL_ACCESS_TOKEN = ""

USER_ID = "USER_LINE_ID"

# ③ Google Drive の「temp」フォルダID
FOLDER_ID = ""

def get_drive_images():
    """
    Google Drive の temp フォルダ内の画像リストを取得し、画像の `id` をリストで返す
    """
    query = f"'{FOLDER_ID}' in parents and mimeType contains 'image/' and trashed=false"
    results = drive_service.files().list(q=query, fields="files(id, name)").execute()
    files = results.get("files", [])

    if not files:
        print("No images found in the Drive folder.")
        return None

    return [file["id"] for file in files]

def get_drive_image_url(file_id):
    """
    Google Drive のファイルIDを公開URLに変換する
    """
    try:
        # ファイルのアクセス権限を「公開」に設定
        drive_service.permissions().create(
            fileId=file_id,
            body={"role": "reader", "type": "anyone"},
        ).execute()

        # 画像のダウンロードURLを取得
        return f"https://drive.google.com/thumbnail?id={file_id}&sz=w1000"
    except Exception as e:
        print(f"Error making image public: {e}")
        return None

def send_image(image_url):
    """
    LINE API を使い、取得した画像URLを LINE ユーザーに送信する
    """
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {LINE_CHANNEL_ACCESS_TOKEN}"
    }

    payload = {
        "to": USER_ID,
        "messages": [
            {
                "type": "image",
                "originalContentUrl": image_url,
                "previewImageUrl": image_url
            }
        ]
    }

    response = requests.post(LINE_PUSH_URL, headers=headers, json=payload)
    print(response.status_code, response.text)

def main():
    """
    画像リストを取得し、ランダムな画像を選んでLINEに送信
    """
    image_ids = get_drive_images()
    if not image_ids:
        print("No images found, skipping LINE push.")
        return

    # 画像をランダムに選択
    random_image_id = random.choice(image_ids)
    image_url = get_drive_image_url(random_image_id)

    if image_url:
        # LINE に画像を送信
        send_image(image_url)
    else:
        print("Failed to get a valid image URL.")

# スケジュール実行(30分ごと)
if __name__ == "__main__":
    while True:
        main()
        time.sleep(1800)  # 30分ごとに実行

として再度実行すると

400 {"message":"The property, 'to', in the request body is invalid (line: -, column: -)"}

よくみたらユーザIDを設定忘れていた…

import requests
import random
import time
from googleapiclient.discovery import build
from google.oauth2 import service_account

# ① Google Drive API の設定
SCOPES = ["https://www.googleapis.com/auth/drive"]
SERVICE_ACCOUNT_FILE = "service_account.json"

creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
drive_service = build("drive", "v3", credentials=creds)

# ② 送信先の LINE API 設定
LINE_PUSH_URL = "https://api.line.me/v2/bot/message/push"
LINE_CHANNEL_ACCESS_TOKEN = ""

USER_ID = ""


# ③ Google Drive の「temp」フォルダID
FOLDER_ID = ""

def get_drive_images():
    """
    Google Drive の temp フォルダ内の画像リストを取得し、画像の `id` をリストで返す
    """
    query = f"'{FOLDER_ID}' in parents and mimeType contains 'image/' and trashed=false"
    results = drive_service.files().list(q=query, fields="files(id, name)").execute()
    files = results.get("files", [])

    if not files:
        print("No images found in the Drive folder.")
        return None

    return [file["id"] for file in files]

def get_drive_image_url(file_id):
    """
    Google Drive のファイルIDを公開URLに変換する
    """
    try:
        # ファイルのアクセス権限を「公開」に設定
        drive_service.permissions().create(
            fileId=file_id,
            body={"role": "reader", "type": "anyone"},
        ).execute()

        # 画像のダウンロードURLを取得
        return f"https://drive.google.com/thumbnail?id={file_id}&sz=w1000"
    except Exception as e:
        print(f"Error making image public: {e}")
        return None

def send_image(image_url):
    """
    LINE API を使い、取得した画像URLを LINE ユーザーに送信する
    """
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {LINE_CHANNEL_ACCESS_TOKEN}"
    }

    payload = {
        "to": USER_ID,
        "messages": [
            {
                "type": "image",
                "originalContentUrl": image_url,
                "previewImageUrl": image_url
            }
        ]
    }

    response = requests.post(LINE_PUSH_URL, headers=headers, json=payload)
    print(response.status_code, response.text)

def main():
    """
    画像リストを取得し、ランダムな画像を選んでLINEに送信
    """
    image_ids = get_drive_images()
    if not image_ids:
        print("No images found, skipping LINE push.")
        return

    # 画像をランダムに選択
    random_image_id = random.choice(image_ids)
    image_url = get_drive_image_url(random_image_id)

    if image_url:
        # LINE に画像を送信
        send_image(image_url)
    else:
        print("Failed to get a valid image URL.")

# スケジュール実行(30分ごと)
if __name__ == "__main__":
    while True:
        main()
        time.sleep(1800)  # 30分ごとに実行

で実行すると問題なく画像が表示される

次はこれをモジュールにしてLINEのメッセージ送信と組み合わせる

GoogleDriveの指定フォルダからファイルを取得し処理

GoogleDriveの指定フォルダからファイルを取得し処理

Google Drive API でフォルダのIDを取得できたので
次にここにpdfファイルを配置して
これを取得しテキストを抽出する

PyPDF2 と pdfminer.six はどちらも PDF ファイルを操作するための Python ライブラリ

PyPDF2 には PDF ファイルの結合、分割、ページの回転、ページの追加・削除など、PDF のページ操作機能が充実

pdfminer.six は PDF を読み取ってテキストデータを抽出することが主目的のため、ページ操作や編集の機能はない

PyPDF2 は比較的軽量で、簡単なテキスト抽出やページ操作には高速に動作しますが、PDFの内容解析が簡易的

pdfminer.six はテキスト抽出においては非常に詳細な処理を行うため、
特に長い PDF や複雑なレイアウトのファイルでは処理時間が長くなる

ということでpdfminer.sixを使いテキストを取り出す

そしてその処理結果を渡すというモジュールを作成する

Google Drive の
School フォルダの ID: ここからpdfファイルを取得し
pdfminer.sixを使ってPDFからテキストを抽出したい

Google Drive の School フォルダの ID:
ここからpdfファイルを取得し pdfminer.sixを使ってPDFからテキストを抽出したい
個人で使うので token.jsonを使ったコードにする
抽出したテキストを処理する関数に渡すようにするため 抽出したテキストをOllamaに送信は不要。

import os
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
from googleapiclient.http import MediaIoBaseDownload
from pdfminer.high_level import extract_text
from io import BytesIO

# Google Drive APIの認証
def authenticate_drive():
    SCOPES = ['https://www.googleapis.com/auth/drive']
    creds = None
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    else:
        print("認証トークンが見つかりません。認証を実行してください。")
        return None
    return build('drive', 'v3', credentials=creds)

# フォルダ内のPDFファイルリストを取得
def list_pdf_files_in_folder(service, folder_id):
    """Google Driveフォルダ内のPDFファイルのリストを取得します"""
    query = f"'{folder_id}' in parents and mimeType='application/pdf'"
    results = service.files().list(q=query, fields="files(id, name)").execute()
    files = results.get('files', [])
    return files

# Google DriveからPDFファイルをダウンロード
def download_pdf_from_drive(service, file_id):
    """Google DriveからPDFファイルをダウンロードし、バイナリデータを返します"""
    request = service.files().get_media(fileId=file_id)
    file_data = BytesIO()
    downloader = MediaIoBaseDownload(file_data, request)
    done = False
    while not done:
        status, done = downloader.next_chunk()
        print(f"Download Progress: {int(status.progress() * 100)}%")
    file_data.seek(0)
    return file_data

# PDFからテキストを抽出
def extract_text_from_pdf(pdf_data):
    """PDFファイルデータからテキストを抽出します"""
    text = extract_text(pdf_data)
    return text

# テキストを処理する関数
def process_text(text):
    """抽出したテキストを処理します"""
    print("Extracted Text:\n", text)
    # テキストの処理ロジックをここに追加します

# フォルダ内のPDFを処理
def process_pdfs_in_folder(folder_id):
    service = authenticate_drive()
    if not service:
        return

    pdf_files = list_pdf_files_in_folder(service, folder_id)
    if not pdf_files:
        print("指定されたフォルダにPDFファイルが見つかりません。")
        return

    for pdf_file in pdf_files:
        print(f"Processing file: {pdf_file['name']}")
        pdf_data = download_pdf_from_drive(service, pdf_file['id'])
        pdf_text = extract_text_from_pdf(pdf_data)
        if pdf_text:
            process_text(pdf_text)
        else:
            print(f"{pdf_file['name']} からテキストを抽出できませんでした。")

# フォルダIDを指定して処理を開始
folder_id = ""  # SchoolフォルダのID
process_pdfs_in_folder(folder_id)

これを

python get_pdf_read.py

で実行するとかなりの精度で取り出すことができた

次はこれをモジュールにしてollamaに処理結果を渡せるようにする

なおコード解説は
コードの説明
1. authenticate_drive関数: token.jsonを使用してGoogle Drive APIに認証し、Driveサービスを取得
2. list_pdf_files_in_folder関数: 指定したフォルダID内のPDFファイルをリスト化
3. download_pdf_from_drive関数: 各PDFファイルをダウンロードし、バイナリデータを返す
4. extract_text_from_pdf関数: pdfminer.sixを使ってPDFデータからテキストを抽出
5. process_text関数: 抽出したテキストを処理します。ここに任意の処理ロジックを追加
6. process_pdfs_in_folder関数: フォルダ内の全PDFファイルを処理し、各PDFのテキストを抽出後にprocess_text関数に渡す
これで、抽出されたテキストを直接処理する

なおGPTで生成したコードでは

SCOPES = ['https://www.googleapis.com/auth/drive.readonly']

というスコープだが
これだと認証エラーになる

    SCOPES = ['https://www.googleapis.com/auth/drive']

で回避可能

これは google カレンダーの時も同様で

SCOPES = ['https://www.googleapis.com/auth/calendar']

なら認証エラーにならない

Ubuntu 14.04 で GoogleDrive 同期

Ubuntu 14.04 で GoogleDrive 同期

Google Drive のクライアントアプリは
Windows と Mac ではあっても
Linux 系はないため、かわりに同期するアプリを使います

今回は
Grive Tools をインストールします

まず
Ctrl + Alt + t で端末を開きます

次に

sudo add-apt-repository ppa:thefanclub/grive-tools

でリポジトリを追加します

次に

 sudo apt-get update

でリポジトリを更新します

そして

sudo apt-get install grive-tools

でインストールします

インストールが終わったら
画面左上のダッシュボードから
grive で検索し
Grive setup をクリックします

grive

すると設定画面になるので
次 をクリックします

grive2

すると
Grive が アクセス許可を求めてくるので
許可をクリックし
次のページで
Google Drive コードが表示されるので
それを入力し、次をクリックします

grive3

するとインストールが完了となり
現在Google Drive にあるファイルとの自動同期が行われます

grive4

USBを経由したりする必要がなく
Macとの同期も簡単にできるので
おすすめです