スプレッドシートをGoogle App Scriptを使用してAWS S3に上げてみた
はじめに
エクセルライクに使えて、誰でも手軽に無料で利用できるスプレッドシート。
今回そのスプレッドシートをCSVの形式でAWS S3にGoogleアップスクリプトを利用してアップロードする方法を紹介します。
CSV形式で保存しておけばそこからPythonを利用して機械学習のデータとして利用できたりと汎用性が高そうです。
前提と注意事項
※ AWSのアカウントの開設やアカウント開設初期の基本的なセキュリティ設定は全て終えている前提です。
※ AWSのUIは頻繁に変わるので執筆時点(2023/02/11)から変わっている場合があります。
IAMを作成する
AWS マネージメントコンソールから、IAMと検索し、IAMのページにアクセス。左サイドバーの「ユーザー」から「ユーザーを追加」をクリック
ユーザー名を「S3-access-user」とし、次へをクリック
「ポリシーを直接アタッチする」を選択し許可ポリシーの フォームから「S3」と打ち込み「AmazonS3FullAcess」のポリシーを選択。「次へ」をクリック
ユーザー名と許可の概要からアタッチしたポリシーを確認し、「ユーザーの作成」を押下
IAMユーザーが作成されていることを確認しユーザー名をクリック
設定項目の「アクセスキーを作成」をクリック
コマンドラインインターフェース(CLI)を選択し確認のラジオボタンにチェックを行い「次へ」をクリック
タグを「GAS」と設定し「アクセスキーを作成」をクリック
必要に応じてCSVファイルをダウンロードしておく。 ここで表示されているアクセスキーとシークレットアクセスキーは控えておく
S3バケットの作成
バケットの作成をクリック
バケット名を「gas-file-access」と入力、AWSリージョンが東京リージョンになっていることを確認する。
「パブリックアクセスを全てブロック」にチェックが入っていることを確認 またバージョニング機能は今回はいらないので無効にするになっていることを確認
「バケットを作成」をクリック
これでS3バケットが作成される。
スプレッドシートの作成とGoogle App Scriptのコーディング
スプレッドシートを作成し、シート名をprice-list
としておく
拡張機能
→Apps Script
を選択
以下のスクリプトを貼り付ける
const accessKey = "XXXXXXXX"; // アクセスキー(IAM作成で控えたもの) const secretKey = "XXXXXXXX"; // シークレットキー(IAM作成で控えたもの) const bucketName = "gas-file-access"; // S3バケット名 const spreadSheetId ="XXXXXXXX"; //スプレッドシートのID function myFunction() { // ライブラリスクリプトID // 1Qx-smYQLJ2B6ae7Pncbf_8QdFaNm0f-br4pbDg0DXsJ9mZJPdFcIEkw_ // スプレッドシートを取得 var ss = SpreadsheetApp.openById(spreadSheetId); // シートのオブジェクトを取得 var sheet = ss.getSheetByName('price-list'); // データ取得 var data = sheet.getRange('A:C').getValues(); // idがあるデータだけ抽出 var csv = ''; for ( var i = 0; i < data.length; i++ ) { if ( data[i][0] != '' ) { csv += '"' + data[i][0] + '","' + data[i][1] + '","' + data[i][2] + '"' + "\n"; } } // バイナリに変換 csv = Utilities.newBlob( csv ); var s3 = S3.getInstance( accessKey, secretKey ); s3.putObject( 'gas-file-access', 'test-data/price-list.csv', csv, {logRequests:true} ); }
スプレッドシートのIDは以下URL部分の*の部分になる
https://docs.google.com/spreadsheets/d/********/edit#gid=0
ライブラリを追加する
モーダルが表示されたらスクリプトI Dを入力し、検索ボタンを押下
スクリプトID:1Qx-smYQLJ2B6ae7Pncbf_8QdFaNm0f-br4pbDg0DXsJ9mZJPdFcIEkw_
ライブラリS3が確認できたら追加を押下
実行ボタンをクリック
S3バケットにデータが挿入される。
所感
結構簡単に実装できるもんだなぁと感心しました。 一方でGoogle App Scirpt上でアクセスキーとシークレットキーを使用する部分があるので共通で編集するファイルなどには今回の実装は含めない方がいいです。
あくまで個人で利用する場合に限定した方が良さそうですね。
Jupyter Labで作成したNotebookをはてなブログに貼り付ける方法
技術系の記事を書くときソースコードいちいち載せるのめんどくさい
勉強したことを見返したり、インプットしたことをそのままにしておいて知識的な便秘にならないように日々しているわけですがいかんせんソースコードの転記がめんどくさい。
Pythonを書く時にはプログラムの対話型実行環境のJupyter Labを使っているわけですがコレそのまま貼り付けられないのかな?と思ったことが今回の記事のネタの出どころになっております。
結論:Jupyter Labで作成したipynbファイルははてなブログにそのまま貼り付け可能
Githubが提供している「GitHub Gist」を使えばできるみたいです。Githubほどversion管理とかするまでもないソースコードを共有するのに便利なサービスのようです。 知らなかった。 世の中便利になってるなぁ
手順
Jupyter Labで作成したファイルを用意する
GitHub Gistにアクセス gist.github.com
中央のソースコード記入のウィンドウにファイルをドラックアンドドロップ
右下のアロー(下三角)ボタンから「create public gist」を選択しボタンを押下
作成内容を確認する
はてなブログに移動し、記事の編集画面の右側にあるサイドバーのプラスボタンをクリック。 Git Gistのラジオボタンをオン(緑)にする。
自分のGithub名を入力して連携ボタンを押す
先ほど貼ったNotebookが出てくるので選択すれば貼り付け完了!
おわりに
たまに自分のアカウントを連携してもNotebookが出てこなかったり、「連携が失敗しました」と出る時があるので一旦下書きしてからページをリロードしたり、連携を解除してから再度してみたりと試してみてもらえればと思います。
うおのめスパイスカレーの作り方
まえがき
我々日本人の国民食カレー。
日本に伝来したカレーは文明開化により伝わったとな。
その後、軍隊や農学校やらで食べられてインスタント食品として固形カレールウとして販売が一般家庭にされるようになってから爆発的に人気に。
今となればスーパーに行けばカレールウが当たり前のように売ってる昨今。
しかしながら一方でそんな「日本のカレー」とはまた違う「スパイスカレー」も最近では人気になってお店も乱立しまくってる。
かくいうカレー好きの僕も街へ出ればしょっちゅうカレーの店に行っている。
ほんとにアホかと思うぐらいよく外で食べてたんですがぶっちゃけ
結構高いんですよねぇ。
個人的にカレー週3でもいいくらいなんで破産確実。
でも食べたい
なら作ったらいいんじゃない?
と無職的な発想で作りはじめたカレーが...
まあうまいこと。
材料のスパイスは100均でそろうし、ルウのように油まみれでもないので、脂質気にしてる人でも食べやすい。
それでは...過去に食べた有名店のカレーを先に貼っておき、自分で非常にやりずらい状況にしておきながら何なのですが、うおのめスパイスカレーの作り方をご覧あれ。
材料
(非常にざっくりです)
- 鶏もも肉:1枚
- 玉ねぎ:1個
- カットトマト缶:半分
- クミンパウダー:大さじ1/2
- コリアンダーパウダー:大さじ1/2
- チリペッパー:小さじ1/2(辛味なので好みに合わせて調節)
- ターメリックパウダー:小さじ1/2
- にんにくチューブ:5cm
- しょうがチューブ:5cm
- 塩:小さじ1/2
- 水:250ccくらい
- 香菜:1株〜3株
- オリーブオイル:大さじ1.5
作り方
先に材料を切っておきましょう
玉ねぎをみじん切りに
パクチーは葉の部分をざっくりと3~5cmぐらいで切っておき、根の部分と葉を分けておきます。
根はみじん切り
写真撮り忘れたけど、鶏肉も皮をとって一口の大きさで切っておく
少し深めのフライパンにしっかりとオリーブオイルを大さじ1.5くらい入れて、強火であためて、煙がたつくらいになったらパクチーの根っこを入れる
ちなみにサラダ油でもいいんですが、個人的に体に悪いのでオリーブオイルを使用してます。
続いて玉ねぎを投入
焦げ付きが気になって火を弱めたくなりますが、あんまり気にせず炒めましょう
カットトマト缶を投入。市販の100円以下で売ってるやつを半分くらいを目分量で入れてしまいましょう
トマトの水分が飛んでペーストに近くなってきたらスパイスと塩を投入
一回火を止めてから入れたほうがいいです。
なぜなら火をつけたままにしておくと、スパイスが煙と共に巻き上がり、思い切り吸い込んでカレーどころじゃなくなります。
鶏肉を投入。このあとしっかり煮込むので、表面が白っぽくなるくらいまでスパイスまみれにするくらいまで混ぜ合わせればおけ
水を250cc投入し15分ほど弱火で煮込む
仕上げにパクチーの葉を投入して軽く混ぜ合わせたら完成!
盛り付ければ完成!
かんたんな割に本格的スパイスカレー
カレー粉とか一切使っていないのに本格的なスパイスカレーが爆誕しました。
味のあるものといえば塩や鶏肉の油ぐらい?ですかね?
最後の盛り付けの写真が雑で映えない感じになってますがほんとおいしいんですよ。
一人暮らしの友人に教えたらガチで週3でこのカレーを作って食べてました。
ちなみに僕はパクチーは市販で買うと高いので去年の秋口くらいから家庭菜園で作ってます。
市販のものに比べたら痩せ細ってましたが、しっかりと風味が出ていて種自体も安いのでオススメです!
スパイスさえ買ってしまえばあとは1回分の鶏肉だけあればいつでも作れるのでぜひ暇な人は作ってみて感想を教えてください。
是非、お試しあれ!
Go ステートメント編
ステートメント編
if文
基本のif文
package main import "fmt" func main(){ num := 6 // 余剰がなければ"by 2" if num % 2 == 0 { fmt.Println("by 2") }
if else文
package main import "fmt" func main(){ num := 6 // 余剰がなければ"by 2"あれば"else" if num % 2 == 0 { fmt.Println("by 2") } else { fmt.Println("else") }
else if文
pacage main import "fmt" func main() { num := 6 // 2で割って余剰がなければ"by 2" // 3で割って余剰がなければ"by 3" // 余剰があれば"else" if num % 2 == 0 { fmt.Println("by 2") } else if num % 3 == 0 { fmt.Println("by 3") } else { fmt.Println("else") } }
論理演算子
package main import "fmt" func main() { x, y := 11, 12 // 論理積(左辺も右辺もtrueの場合にtrue) if x == 10 && y ==10{ fmt.Println("&&") } // 論理和(左辺か右辺のどちらかがtrueの場合にtrue) if x == 10 || y ==10{ fmt.Println("||") } // 論理否定 if x != 10 { fmt.Println("||") }
if文 関数の呼び出し
// 2で割った際の余剰を判定 func by2(num int) string{ if num % 2 == 0 { return "ok" } else { return "no" } } func main() { // 関数を変数resultに挿入してif文を実行 result := by2(10) if result == "ok" { fmt.Println("great") } // セミコロンを使用してワンライナーで記述も可能 if result2 := by2(10); result2 == "ok"{ fmt.Println("great 2") } }
for文
for文の基本
package main import "fmt" func main(){ // i が10 以上になるまで繰り返す for i := 0; i < 10; i++ { fmt.Println(i) }
continue
package main import "fmt" // continue for i :=0; i < 10; i++ { if i == 3{ fmt.Println("continue") continue } fmt.Println(i) }
break
for i :=0; i < 10; i++ { if i == 3{ fmt.Println("continue") continue } if i > 5 { fmt.Println("break") break } fmt.Println(i) }
for文の別の書き方
sum := 1 for ; sum < 10; { sum += sum fmt.Println(sum) } fmt.Println(sum) }
;(セミコロン)の省略も可能
sum := 1 for sum < 10 { sum += sum fmt.Println(sum) } fmt.Println(sum) }
無限ループ
for { fmt.Println("hello") }
switch分
基本的なswich文
package main import "fmt" import "time" func main(){ os := "mac" switch os { case "mac": fmt.Print("Mac!!") case "windows": fmt.Print("Windows!!") default: fmt.Println("Default!!") }
defaultを省略した場合は処理がされないまま終了する
func main() { switch os { case "mac": fmt.Print("Mac!!") case "windows": fmt.Print("Windows!!") } } // >>> Process finished with exit code 0
func getOsName() string{ return "dafdaf" } func main() { switch os := getOsName(); os { case "mac": fmt.Print("Mac!!") case "windows": fmt.Print("Windows!!") default: fmt.Println("Default!!") } } // 外側で変数osを使おうとするとスコープ外でエラーになる fmt.Println(os)
switchのところで条件を書かなくても処理ができる
t := time.Now() fmt.Println(t.Hour()) switch { case t.Hour() < 12: fmt.Println("Morning") case t.Hour() < 17: fmt.Println("Afternoon") } }
defer
遅延実行
package main import "fmt" func main(){ // defer 遅延実行 main関数の処理が終わってからdeferで定義したものが動く defer fmt.Println("world") fmt.Println("hello") } // >>> hello // >>> world
deferを持っている関数を呼び出した場合はその関数内のdeferが終わってからmain関数となる
func foo() { defer fmt.Println("world foo") fmt.Println("hello foo") } func main(){ foo() defer fmt.Println("world") fmt.Println("hello") } // >>> hello foo // >>> world foo // >>> hello // >>> world
deferが並んだ場合は最後のdferから逆順に実行されてゆく
func main(){ fmt.Println("start") defer fmt.Println("1") defer fmt.Println("2") defer fmt.Println("3") fmt.Println("end") } // >>> start end 3 2 1
そもそものdeferの使いどきはいつなのか?
ファイルを取り扱う際に使用したりすることが比較的多い
*lesson.goがある想定で
func main() { file, _ := os.Open("./lesson.go") defer file.Close() data := make([]byte, 100) // バイト配列の作成 file.Read(data) //バイト配列を挿入 fmt.Println(string(data)) // stringにキャストして出力 }
log
基本的にGOはログ出力において他の言語のように INFO やERROR などを組み込んだ標準ライブラリで提供していないのでもし他言語のようにエラーハンドリングを行いたいのであればサードパーティライブラリを使用する必要がある
package main import ("log" "fmt" "os" "io" ) func loggingSettings(logFile string){ logfile, _ := os.OpenFile(logFile, os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) multiLogFile := io.MultiWriter(os.Stdout, logfile) log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) log.SetOutput(multiLogFile) } func main(){ // 基本系 log.Println("logging!") log.Printf("%T, %v", "test", "test")
Fatal
Fatal以降は処理が終了するので後続のコードは実行されない
log.Fatalf("%T, %v", "test", "test") // 以降出力されない log.Fatalln("error!!") // 出力されない fmt.Println("ok!")
// 存在しないファイルを開こうとする _, err := os.Open("fdfdfdfd") if err != nil{ log.Fatalln("Exit", err) } // >>> Exit open fdfdfdfd: no such file or directory }
エラーハンドリング
他の言語のようにtry exceptのようにハンドリングする訳ではなく、使用した関数においてerrが返却されるものに対してひとつひとつに対してif文でエラーハンドリングするのがGoの作法としてある
package main import ("log" "os" ) func main(){ file, err := os.Open("./lesson.go") if err != nil { log.Fatalln("Error!") } defer file.Close() data := make([]byte, 100) count, err := file.Read(data) if err != nil{ log.Fatalln("Error") } fmt.Println(count, string(data)) // err 変数が2回目のshort decraretionのinisirazeとなっているが、どれかひとつinisirazeされていればエラーにならない if err = os.Chdir("test"); err != nil{ log.Fatalln("Error") } }
panicとrecover
panicやrecoverを使用することは実はあまり推奨されていない。 エラーハンドリングをきちんとしようという考えからきている
package main import "fmt" func thirdPartyConnectedDB(){ panic("Unable to connect database!") } func save(){ thirdPartyConnectedDB() defer func(){ s := recover() fmt.Println(s) } } func main(){ save() fmt.Println("OK?") } // >>> Unable to connect database ! // >>> thirdPartyConnectedDBメソッドでパニックが起こる
ThirdPartyConnectedDBメソッドでパニックが起こるが、defer 文は panic() 関数が呼ばれるかに関わらず処理される
package main import "fmt" func thirdPartyConnectedDB(){ panic("Unable to connect database!") } func save(){ defer func(){ s := recover() fmt.Println(s) } thirdPartyConnectedDB() } func main(){ save() fmt.Println("OK?") } // >>> Unable to connect database ! // >>> OK?
Go ポインタ編
Go ポインタ編
ポインタ
package main import "fmt" func main(){ var n int = 100 fmt.Println(n) // 100 // &(アンパサンド)はメモリのアドレスを表す fmt.Println(&n) // >>> 0xc00001a040 (変数nのメモリアドレス) // *intでポインタ変数の定義ができ、変数への格納はメモリアドレスを渡す var p *int = &n fmt.Println(p) // >>> 0xc00001a040 // *ポインタ変数 で実体値にアクセスできる(デリファレンスと読んだりもする) fmt.Println(*p) // 100 }
*デリファレンス(dereference)
何かを参照しているものの参照先を見に行くこと。
変数とポインタ変数の挙動の違い
func one(x int){ x = 1 } func main(){ var n int = 100 one(n) // func oneでは書き換えが行われずに100 fmt.Println(n) // >>> 100 }
func two(x *int){ *x = 1 // *xで実体(n)にアクセスし書き換え } func main(){ var n int = 100 two(&n) // 変数nのメモリのアドレスを渡す fmt.Println(n) // >>> 1 }
new
値を入れずにメモリにアドレスだけを確保したい場合はnewを使う
package main import "fmt" func main(){ // メモリにアドレスだけを確保したい場合はnewを使う var p *int = new(int) fmt.Println(p) // >>>0xc0000a0000 // 中身の実体値は0が入っている fmt.Println(p) // >>> 0 // newをしない場合はポインタ型変数の宣言をしてもメモリを確保がされていない var p2 *int fmt.Println(p2) // >>> nil // 中身を確認すると panicでerrorが返却される fmt.Println(*p2) }
struct
package main import "fmt" // capital paburiku // プライベートになる type Vertex struct{ X int Y int } func main() { v := Vertex{X:1, Y:2} fmt.Println(v) // vの中にもアクセスできる fmt.Println(v.X, v.Y) // 中身の書き換え v.X = 100 fmt.Println(v.X, v.Y) type Vertex2 struct{ X int Y int S string }
func main() { v := Vertex{X:1, Y:2} fmt.Println(v) // vの中にもアクセスできる fmt.Println(v.X, v.Y) // 中身の書き換え v.X = 100 fmt.Println(v.X, v.Y) // 指定していなければデフォルトのint 0が挿入される(stringであれば空) v2 := Vertex{X: 1} fmt.Println(v2) // >>> {1 0} // structの中身を指定しない場合は順番通り入れればOK v3 := Vertex2{1, 2, "test"} fmt.Println(v3) // >>> {1 2 test} // 全て指定しなければデフォルトで返却される v4 := Vertex2{} fmt.Println(v4) // >>> {0 0 } fmt.Printf("%T %v\n", v4, v4) // nilにはならない sliceやmapはnilになる var v5 Vertex2 fmt.Printf("%T %v\n", v5, v5) v6 :=new(Vertex2) fmt.Printf("%T %v\n", v6, v6) // pointerが帰ってきている v7 := &Vertex2{} // こっちを使う人の方が多い気がする fmt.Printf("%T %v\n", v7, v7) // pointerが帰ってきている s := []int{} s := []int{} }
2023年 新年のご挨拶
あけましておめでとうございます
すでに1月も今日で11日になってしまってはいますが、あけましておめでとうございます。
うおのめマリーを本年もよろしくお願いいたします🐟
ブログ自体も始めたばかりで、Twitterも繋がりが薄く、しかも放置気味・・・という中で当ブログを見てくださる貴重な方々、ヒマな無職な方々、ほんとうにありがとうございます。
ご報告
釣りYoutuberのようなタグを出していてなんなのですが、
実はご縁があって本年2023年より正社員で仕事はじめました。
この報告聞いてブログだけ見た人はなんのこっちゃ?となりますね。
Twitterではいちおう「無職エンジニア」と肩書きをつけてのんびりと活動しているので、やっぱり形として無職ではなくなったから外さなきゃダメかなと自問自答したり、思考を無限の彼方へ飛ばしたりしてウンウン唸ったりしていましたが、一旦ここでカミングアウト(?)することに決めました。
そして「無職エンジニア」の肩書き外すか考えたんですが、外さないことにしました。
理由としては、無職の頃とやること変わらないからなんです。
実は無職の時でもエンジニアとしての活動を完全に止めていたわけではなくて、プログラミングの先生のようなメンター活動や就職活動用のポートフォリオのインフラ構築を請け負ったりなど、手伝いで小銭稼ぎみたいなことはしてたんですね。
今度の仕事は一応正社員であるものの、働き方が裁量労働制となっておりまして、自分の裁量である程度融通の効いた働き方ができるのが無職の時とあんま変わらないなぁと新年明けてから少し働いてみて思ったところが詳細な理由ですね。
あとは純粋にいつまた無職になるかわからないからそのままにしておこうかなと笑
まとめ
いきなり新年から肩書きうんぬんのかなりどうでもいい話ことを徒然なるままに書き綴りました。
基本的にはお話しした通り、事実としては無職ではないけれども、スタンスと言葉はそのまま「無職エンジニア」としていこうという感じですので何卒よろしくとお願いいたしまして新年のあいさつとさせていただきます。
目標達成するためのマンダラチャートの書き方
2022年も残り少なくなってきました。
僕自身今年をふり返ってみるとAWSのソリューションアーキテクトの資格を取得したり、家庭菜園を始めたり、はたまた突然無職になったり・・・いろいろなことがあった1年でした。
この1年をふりかえってみると、他にもやりたいことが頭の中にあって「これをやりたい!」と思っていたものの、忙殺される日々を過ごしているうちに忘れてしまっていて、あれ?なんて気がついた時にはもう年末。一体今年は何してたんだ?と思うことも当然あります。
来年こそ目標を達成する年にするため、年始にいきなりきめるとなるとなかなかすぐに浮かばないので今のうちからぼんやり考えるといいかもですね。
そんな時には「マンダラチャート」を使って可視化するのがオススメです。
マンダラチャートとその書き方
マンダラチャートとは9×9のマスで構成される表をさらに3×3で区切り、中心の3×3マスの中央に成し遂げたい目標、その中心周りの3×3マスには必要な要素を書き、その周りの3×3のマスにその目標を達成するためのアイデアや要素などを記入してゆく目標設定手法です。
文字で説明するとなんかわかりづらいですけど、実際の表を見てもらうとなんとなくわかっていただけるんじゃないかなと思います。
ステップ的には3つ
- 最中央に達成したい目標を書く
- 目標に必要な要素8つを書く
- その必要なことにさらに必要な要素を8つ書く
あと、8つも要素うめれないという人は全部埋めなくても全く問題ないです。
書ける範囲で無理せず書いてみると良いと思います。
思考の整理
自身の思考の整理になるところがこのチャートを書くメリットですね。
目標達成に必要な要素とその要素に必要な要素...と行動目標が細分化されているのでじゃあ取り組みするときに具体的に何をするのかということを考えやすいというのもあります。
人は目標を大きくしがちなので細分化して少しずつ達成してゆくというのを可視化できるのはメリットになりうる部分じゃないかなと思うところです。
2023年の僕のマンダラチャート
とはいえ2023年も無理せずにいきましょうね
2~3日かけて書いてみたのですが、こうして書いてみると意外と書けないものです。 なるべく具体的に書いてみたものの(貯金をするなどはまだ具体的に決めていない)再考や追加が必要になりそうです。
もし年末、お時間が取れそうでしたらマンダラチャートぜひオススメです。
こちらのアプリが直接書き込みできて使いやすいのでぜひ使ってみてください
apps.apple.com