LoginSignup
sami1220
@sami1220

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

DataFrameをcsv化して共有ドライブに保存する方法がわからず困ってます

解決したいこと

PandasのDataFrameをcsvに変換し、共有ドライブに保存したい

共有ドライブから取得したスプレッドシートをpythonで一部書き換えて、新ファイルとして共有ドライブに保存する方法がわからず困っております。

以下、ソースコードとやりたいことについて記載してます。

1. DriveAPI、OAuth認証で共有ドライブにアクセス
2. 共有ドライブ内のスプレッドシート(file_id)を取得(df)
3. dfをdc.df_convert(df)で内容を一部書き換えてdf_へ
4. df_をcsvに変換し、元々の共有ドライブに保存したい ←この方法がわからず困ってます

該当するソースコード

from __future__ import print_function
import io
import os.path
import pandas as pd
from googleapiclient.http import MediaIoBaseDownload
from convert_dataframe import DataConvertion
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from googleapiclient.http import MediaFileUpload

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

def main():
    creds = None
    file_id = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'#共有ドライブ内のスプレッドシート
    folder_id = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx' #編集後、保存先のフォルダ(共有ドライブ内)
    mime_type = 'text/csv'

    if os.path.exists('token_2.json'):
        creds = Credentials.from_authorized_user_file('token_2.json', SCOPES)
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'client_secret.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # 次の実行のために認証情報を保存する
        with open('token_2.json', 'w') as token:
            token.write(creds.to_json())

    try:
        service = build('drive', 'v3', credentials=creds)

        request = service.files().export_media(fileId=file_id, mimeType=mime_type)
        fh = io.BytesIO()
        downloader = MediaIoBaseDownload(fh, request)
        done = False

        while done is False:
            status, done = downloader.next_chunk()
        #共有ドライブ内のスプレッドシートを取得
        df = pd.read_csv(io.StringIO(fh.getvalue().decode()))
        dc = DataConvertion()#別のモジュールでdfの内容を一部書き換え
        df_ = dc.df_convert(df)#変換した後のdf_をcsvにして元の共有ドライブに保存したい

    except HttpError as error:
    # TODO(developer) - Handle errors from drive API.
        print(f'An error occurred: {error}')

自分で試したこと

以下のようなコードで実装しているケースは見つかりましたが、以下の場合だと、保存してあるデータをpath部分で指定して、そのデータを共有ドライブに保存するようなケースの場合であったため、今回は採用できそうにないと感じました。

今回やりたいことのポイントとしては、保存してあるデータを呼び出し、書き換えて(df_)新たに新ファイルとして保存する方法なので、その方法がなかなか見つからず困っておりました。

ちなみに、ローカルへ一旦保存や、マイドライブに保存するパターン以外で実装したいと思っております。

わかりにくい部分もあるかと思いますので、恐れ入りますがどうぞよろしくお願いいたします。

media = MediaFileUpload(path, mimetype=mime_type, resumable=True)

file_metadata = {
            'name': 'marketing_report_lead_data',
            'mimeType': mime_type,
            'parents': [folder_id] 
            }
file = service.files().create(body=file_metadata, media_body=media).execute()
0

1Answer

修正ポイント

  • 共有ドライブへデータをアップロードするためには、Drive APIの"Files: create"でsupportsAllDrivesを使用する必要があります。Ref
  • MediaFileUploadは、ファイルパスの代わりにデータを直接しようすることができます。Ref

これらをスクリプトへ反映すると、下記のようになります。

サンプルスクリプト

service = build("drive", "v3", credentials=creds)

csv_data = "a1,b1,c1\na2,b2,c2" # Sample CSV data as string.
folder_id = "###" # Folder ID.

media = MediaIoBaseUpload(io.BytesIO(csv_data.encode("utf-8")), mimetype="text/csv", resumable=True)
file_metadata = {
    "name": "sample filename",
    "mimeType": "application/vnd.google-apps.spreadsheet",
    "parents": [folder_id]
}
file = service.files().create(body=file_metadata, media_body=media, fields="id", supportsAllDrives=True).execute()
print(file)
  • 使用する際は、csv_dataをご使用のデータに置き換えて使用してください。
1

Comments

  1. @sami1220

    Questioner
    ご回答、ご丁寧にありがとうございます。
    まさに、ぴったりの手法をご提案いただき助かりました。
    io.BytesIO(csv_data.encode("utf-8")で渡してあげる方法が使えたんですね〜!
    別の機会でも参考になります。

Your answer might help someone💌