helper.spreadsheet のソースコード

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
google Spreadsheetを操作する

参考資料料

Todo:
    - docstringを整える
"""
import sys
import copy
import pyperclip
from urllib.parse import urlparse  # URLパーサー
from dataclasses import dataclass
import json
import gspread


[ドキュメント] @dataclass(frozen=True) class SpreadsheetValue: """ スプレッドシート値オブジェクト """ json_keyfile_name: str workbook_name: str worksheet_name: str data: list def __init__(self, json_keyfile_name, workbook_name, worksheet_name, data): """ 完全コンストラクタパターン :param json_keyfile_name: str spreadsheetアクセス用シークレットキーファイル名 :param workbook_name: str ブック名 :param worksheet_name: str シート名 :param data: list スプレッドシートの値リスト """ if json_keyfile_name is not None: object.__setattr__(self, "json_keyfile_name", json_keyfile_name) if workbook_name is not None: object.__setattr__(self, "workbook_name", workbook_name) if worksheet_name is not None: object.__setattr__(self, "worksheet_name", worksheet_name) if data is not None: object.__setattr__(self, "data", data)
[ドキュメント] class Spreadsheet: """ スプレッドシートのユーティリティ * スプレッドシートを読み書きする * SpreadsheetValueを生成する * SpreadsheetValueをファイルに保存したり読み込んだりする """ value_object: SpreadsheetValue = None json_keyfile_name: str = None workbook_name: str = None worksheet_name: str = None data: list = None workbook: gspread.Worksheet = None worksheet: gspread.Spreadsheet = None is_google_colabo: bool = False def __init__(self, target_value=None, workbook_name=None, worksheet_name=None): """ コンストラクタ :param target_value: str 対象となるサイトURL、または、SpreadsheetValue 値オブジェクト :param workbook_name: str 対象となるブック名 :param worksheet_name: str 対象となるシート名 """ if target_value is not None: if isinstance(target_value, SpreadsheetValue): value_object = target_value self.value_object = value_object if value_object.json_keyfile_name is not None: self.json_keyfile_name = value_object.json_keyfile_name if value_object.workbook_name is not None: self.workbook_name = value_object.workbook_name if value_object.worksheet_name is not None: self.worksheet_name = value_object.worksheet_name if value_object.data is not None: self.data = value_object.data self.open() else: if isinstance(target_value, str): self.json_keyfile_name = target_value if workbook_name is not None: self.workbook_name = workbook_name if worksheet_name is not None: self.worksheet_name = worksheet_name self.open() self.data = self.worksheet.get_all_values() self.value_object = SpreadsheetValue(self.json_keyfile_name, self.workbook_name, self.worksheet_name, self.data, ) def __eq__(self, other): if not isinstance(other, Spreadsheet): return False return (self.json_keyfile_name == other.json_keyfile_name and self.workbook_name == other.workbook_name and self.worksheet_name == other.worksheet_name and self.data == other.data) # self.dataはSpreadsheetクラスが持つデータ属性を想定
[ドキュメント] def open(self): """ spreadsheetにアクセスして、spreadsheet_valueを更新する :return: bool 成功/失敗=True/False """ if 'google.colab' in sys.modules: self.is_google_colabo = True from google.colab import auth from oauth2client.client import GoogleCredentials auth.authenticate_user() gc = gspread.authorize(GoogleCredentials.get_application_default()) print("google_colab") else: from oauth2client.service_account import ServiceAccountCredentials scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive'] credentials = ServiceAccountCredentials.from_json_keyfile_name(self.json_keyfile_name, scope) gc = gspread.authorize(credentials) print("Not google_colab") self.workbook = gc.open(self.workbook_name) self.worksheet = self.workbook.worksheet(self.worksheet_name) return True
[ドキュメント] def get_value_objects(self): """ 値オブジェクトを取得する :return: crawling_value 値オブジェクト """ return copy.deepcopy(self.value_object)
[ドキュメント] def get_result_data(self): """ スプレッドシートの値リストを取得する :return: spreadsheet_value.data スプレッドシートの値リスト """ return copy.deepcopy(self.value_object.data)
[ドキュメント] def create_save_text(self): """ 保存用文字列の作成 :return: str 保存用文字列の作成 """ buff = self.json_keyfile_name + '\n' # シークレットファイル名追加 buff += self.workbook_name + '\n' # ブック名追加 buff += self.worksheet_name + '\n' # シート名追加 buff += json.dumps(self.data, ensure_ascii=False) + '\n' # データ追加 return buff
[ドキュメント] def clip_copy(self): """ クリップボードにコピーする :return: bool 成功/失敗=True/False """ if self.value_object is None: return False buff = self.create_save_text() pyperclip.copy(buff) # クリップボードへのコピー return True
[ドキュメント] def save_text(self, save_path): """ データをファイルに、以下の独自フォーマットで保存する * シークレットファイル名 * ブック名 * シート名 * データ :param save_path: str セーブする独自フォーマットなファイルのパス :return: bool 成功/失敗=True/False """ if self.value_object is None: return False with open(save_path, 'w', encoding='utf-8') as work_file: buff = self.create_save_text() work_file.write(buff) # ファイルへの保存 return True
[ドキュメント] def load_text(self, load_path): """ 独自フォーマットなファイルからデータを読み込み、スプレッドシートを開く :param load_path: str ロードする独自フォーマットなファイルのパス :return: bool 成功/失敗=True/False """ with open(load_path, 'r', encoding='utf-8') as work_file: buff = work_file.readlines() self.json_keyfile_name = buff[0].rstrip('\n') del buff[0] self.workbook_name = buff[0].rstrip('\n') del buff[0] self.worksheet_name = buff[0].rstrip('\n') del buff[0] self.data: list = json.loads(buff[0].rstrip('\n')) self.value_object = SpreadsheetValue(self.json_keyfile_name, self.workbook_name, self.worksheet_name, self.data, ) self.open() return True
[ドキュメント] def clear_worksheet(self): self.worksheet.clear()
[ドキュメント] def write_dict_columns(self, value_dict, offset=(1, 1), ): """ 辞書を、スプレッドシートのデータとして列方向で書き込む :param value_dict: dict 書き込むデータ(キーも書き込む) :param offset: tuple (row, col)オフセット :return: 書き込んだセル数 """ row, col = offset num = 0 for key, col_list in value_dict.items(): value_list = copy.deepcopy(col_list) value_list.insert(0, key) num += self.write_list_columns(value_list, (row, col)) col += 1 return num
[ドキュメント] def write_dict_rows(self, value_dict, offset=(1, 1), ): """ 辞書を、スプレッドシートのデータとして行方向で書き込む(キーも書き込む) :param value_dict: dict 書き込むデータ :param offset: tuple (row, col)オフセット :return: 書き込んだセル数 """ row, col = offset num = 0 for key, row_list in value_dict.items(): value_list = copy.deepcopy(row_list) value_list.insert(0, key) num += self.write_list_rows(value_list, (row, col)) row += 1 return num
[ドキュメント] def write_list_columns(self, value_list, offset=(1, 1), ): """ リストを、スプレッドシートの列データとして書き込む :param value_list: list 書き込むデータ :param offset: tuple (row, col)オフセット :return: 書き込んだセル数 """ row, col = offset num = len(value_list) cell_str = gspread.utils.rowcol_to_a1(row, col) + ":" + gspread.utils.rowcol_to_a1(row + num, col) cell_list = self.worksheet.range(cell_str) for (cell, value) in zip(cell_list, value_list): cell.value = value self.worksheet.update_cells(cell_list, value_input_option="USER_ENTERED") return num
[ドキュメント] def write_list_rows(self, value_list, offset=(1, 1), ): """ リストを、スプレッドシートの行データとして書き込む :param value_list: list 書き込むデータ :param offset: tuple (row, col)オフセット :return: 書き込んだセル数 """ row, col = offset num = len(value_list) cell_str = gspread.utils.rowcol_to_a1(row, col) + ":" + gspread.utils.rowcol_to_a1(row, col + num) cell_list = self.worksheet.range(cell_str) for (cell, value) in zip(cell_list, value_list): cell.value = value self.worksheet.update_cells(cell_list, value_input_option="USER_ENTERED") return num
if __name__ == '__main__': # インポート時には動かない target_url = "" # 引数チェック if 2 == len(sys.argv): # Pythonに以下の2つ引数を渡す想定 # 0は固定でスクリプト名 # 1.対象のURL target_url = sys.argv[1] elif 1 == len(sys.argv): # 引数がなければ、クリップボードからURLを得る paste_str = pyperclip.paste() if 0 < len(paste_str): parse = urlparse(paste_str) if 0 < len(parse.scheme): target_url = paste_str # クリップボードが空なら、デフォルトURLを用いる else: print('引数が不正です。') sys.exit() print(target_url)