20200726のGoに関する記事は9件です。

[Go言語] 初心者必見シリーズ その1:配列とスライス

配列(Arrays)

配列は 連続したメモリ領域 を確保するデータ構造です。

golang-array.png

定義方法

方法1

構文:

  var 配列名 [サイズ]

例:

  var a [2]string

方法2

構文:

    var  配列名  [サイズ]
        = [サイズ]{初期値1, ..., 初期値n} 

例:

    var a [2]string
        = [2]string{"Hello", "Go"}

NOTE: 宣言と同時に代入する。

方法3

構文:

    配列名 := [サイズ]{初期値1, ..., 初期値n} 

例:

    a := [2]string{"Hello", "Go"}

NOTE: 宣言と同時に代入するなら、方法2よりもこちらが簡潔でおすすめ。

方法4

構文:

    配列名 := [...]{初期値1, ..., 初期値n}

例:

    a := [...]string{"Hello", "Go"}

NOTE: 配列のサイズを "..." にすると、コンパイラーが自動的に要素数を数えてくれる。

多次元配列 (Multiple Dimension)

多次元配列 (Multiple Dimension) は、
[]の数 を増やせば1次元配列と同じように定義することができる。

例:

    a := [][]string{
        {"Hello", "Go"},
        {"Hello", "Python"},
    }

要素の走査 (iteration)

方法1: len関数を利用することで配列のサイズを取得。

for i := 0; i < len(array); i++ {
  fmt.Println(array[i])
}

方法2 : rangeを使って配列を走査。

for index, value := range array{   
    fmt.Println(index, value)
}

まとめ

  • 配列は、宣言後にサイズを変更できません。(固定長

あらかじめデータサイズの確定しないデータには向いてません。

  • 配列は、基本同じ型をもつ要素を格納しますが、

いろんな型を混在させたい場合は interface 型で初期化します。

  array := []interface{}{hello", 10, 2.5}

スライス(Slices)

スライスは可変長の配列の様な動きをするデータ構造です。

定義方法

定義方法は配列と似ていて、サイズを指定せずに宣言します。

方法1

構文:

var 名前 []

例:

var a []string

方法2

構文:

var 名前 [] = []{初期値1, ..., 初期値n} 

例:

var a []string = []string{"Hello", "Go"}

方法3

構文:

名前 := []{初期値1, ..., 初期値n}

例:

a := []string{"Hello", "Go"}

方法4

構文:

名前 := make([], 初期長さ ,初期容量)

例:

a := make([]string, 2, 10)

方法4は、配列にはないような宣言方法。

初期容量はオプション引数。

スライスの構成

golang-slice.png

スライスは配列への参照のようなもの。

要素の走査 (iteration)

走査方法は、配列と同様です。

要素の追加 (append)

スライスへ新しい要素を追加するには、Goの組み込みの append関数 を使います。

要素追加時の動きは、こちらの動画が分かりやすくご覧いただければと思います。

動画(YouTube)

この記事を動画で見たければ、こちらでお願いします。

https://www.youtube.com/watch?v=4IoXc5x6FjA

元記事

https://aifan.jp/2020/07/26/Go%E8%A8%80%E8%AA%9E-%E5%88%9D%E5%BF%83%E8%80%85%E5%BF%85%E8%A6%8B%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E3%81%9D%E3%81%AE%EF%BC%91-%E9%85%8D%E5%88%97%E3%81%A8%E3%82%B9%E3%83%A9%E3%82%A4%E3%82%B9/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Go Test を書いてみよう

Goには標準でテスト機能が組み込まれています。
Goのソースに対して、簡単なテストを書いてみましょう。

環境

% sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.5
BuildVersion:   19F101

% go version
go version go1.14.6 darwin/amd64

サンプルのディレクトリ構造

今回のサンプルファイルは、下記のようになっています。

gotest
  |-- app.go      // アプリの処理 (アプリ名を返す関すのみ持っている)
  |-- app_test.go // app.go の関数をテストする
  |-- main.go     // app.go の関数を呼び出す
main.go
package main

import (
    "fmt"
)

func main() {
    fmt.Println(AppName())
}
app.go
package main

func AppName() string {
    return "My App Name"
}
app_test.go
package main

import (
    "testing"
)

func TestAppName(t *testing.T) {
    expect := "My App Name"
    actual := AppName()

    if expect != actual {
        t.Errorf("%s != %s", expect, actual)
    }
}

アプリの実行確認

まず、アプリが動作することを確認しましょう。アプリ名が出力されています。

% cd gotest
% go run main.go app.go
My App Name

Go のテスト

出力の[path]は、各自環境でのフルパスになります。

% cd gotest
% go test
PASS
ok      [path]/gotest   0.066s

オプション「-v」を付けると、テストの詳細が出力されます。
テストを複数実行する際に、活躍できそうです。

% go test -v
=== RUN   TestAppName
--- PASS: TestAppName (0.00s)
PASS
ok      [path]/gotest   0.135s

Go のテストを書く際の注意点

  1. テストファイル名の終端は「_test.go」でなければならない。
  2. テスト関数名は「Test」で始まらなければならない。
  3. テストファイルは、"testing"パッケージを宣言し、利用する。
  4. デフォルトでassertは用意されていないので、自作する必要がある。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Golangのテストはテーブルドリブンテストで!

はじめに

Golangでテストを書こうとしたときに推奨されてる実装方法としてテーブルドリブンテスト(TableDrivenTests)というものがあります。
このテスト方法はGoの公式のwiki( https://github.com/golang/go/wiki/TableDrivenTests )でも紹介されてるテスト方法です。

テーブルテストとは?

とりあえずにテーブルテスト書いてみます

square.go
package main

// Square 2つのintを受け取りそれらを掛け合わせた物を返す関数
func Square(a, b int) int {
    return a * b
}
square_test.go
package main

import (
    "testing"

    "github.com/stretchr/testify/assert"
)

func TestSquere(t *testing.T) {
    asserts := assert.New(t)
    for _, td := range []struct {
        title  string
        input  []int
        output int
    }{
        {
            title:  "2×3の答えが6になる",
            input:  []int{2, 3},
            output: 6,
        },
        {
            title:  "3×5の答えが15になる",
            input:  []int{3, 5},
            output: 15,
        },
    } {
        t.Run("Square:"+td.title, func(t *testing.T) {
            result := Square(td.input[0], td.input[1])
            asserts.Equal(td.output, result)
        })
    }
}

よくみるテストと違い、

  • name
  • input
  • output

というstructを持った配列をfor文ででまわし様々なテストパターンを確認するというテスト方法です。
このテストの最も大きな利点は複雑な条件のあるテストをテストしやすいという点です。
それは、インプットの値とインプットの値の可読性が高いためどのようなテストパターンを行っていて、どのようなテストパターンをやっていないかを理解しやすいためです。

テストを並行処理する

テストをどんどん書いていくとテスト処理を並行化したいという思いが出てくると思います。
その時はGoのtestingパッケージではt.Parallel()という関数を噛ませてあげることでテストを並行化することができますが、このテーブルテストでやるとバグがおこってしまうそうです。
そのため並行処理を行う時は以下のように書く必要があるそうです
https://github.com/golang/go/wiki/TableDrivenTests#parallel-testing

square_test.go
package main

import (
    "testing"

    "github.com/stretchr/testify/assert"
)

func TestSquere(t *testing.T) {
    t.Parallel() //←追記
    asserts := assert.New(t)
    tests := []struct {
        title  string
        input  []int
        output int
    }{
        {
            title:  "2×3の答えが6になる",
            input:  []int{2, 3},
            output: 6,
        },
    }

    for _, td := range tests {
        td := td //←追記
        t.Run("Square:"+td.title, func(t *testing.T) {
            result := Square(td.input[0], td.input[1])
            asserts.Equal(td.output, result)
        })
    }
}

まとめ

テストの方法には様々な方法がありますが、それぞれのメリットデメリットをしっかり理解してテストコードを書いていきたいですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsとGo言語で簡単なWebアプリを作成する(Dockerで開発環境構築�)

初投稿です。
過去の経験の復習かねて、書き起こしました。

概要

成果物はこちらになりますので、VueやGoを始めてみたい方への力になれれば幸いです。
https://github.com/aocm/vue-go-spa-sample

方針としては、なるべくローカルを汚さないことを最優先に考えました。
開発者ごとに環境が違うのも大変ですし、他のプロジェクト(自分の個人開発・仕事)で他のバージョンを利用していたら思わぬ影響が出てしまうのも嫌ですので、直接ローカルにインストールするのはなるべく避けたいです。

今回はDockerを使って、コンテナ内部で開発環境を作って作業していきます。
ただ、Gitについてはローカル(ホスト)でしか利用できない環境になってしまったので、そこは課題です。
(最低限、更新差分はコンテナの中でも確認できるようにしたほうが親切でした。)

対象読者

  • Dockerは最低限環境構築できているけど、Webアプリ開発ってどこから手を付ければいいんだ?という人
  • Dockerで開発環境を構築するサンプルが見たい人
  • Goの簡単な動くサンプルが見たい人
  • Vueの簡単な動くサンプルが見たい人

※きれいなコードを書くことが目的ではないのでご了承ください

書くこと

  • なるべくローカルを汚さないような開発環境構築の方法
    • ローカルを汚す...ここでは、各プラグインやソフトウェアをローカルにインストールしまくる状態を指します
    • プロジェクトによってバージョンが違うなど色々障害があると思うので、それの対策です
  • 簡単なアプリ作成
    • VueのSPAサンプル
    • Go(Echo)のREST APIサーバー
  • (おまけ)クロスコンパイルしてWindows上でやまびこアプリを起動してみる

ここで書かないこと

  • 本番環境のセッティング、CI/CD、テスト
  • 本番環境を意識したリスク管理
  • 言語の基礎説明
  • Git、GitHubの基本的な使い方

※気が向いたら、時間をつくって別の記事に書くかもしれません

必要事項

  • Docker・docker-composeが使えること
  • Gitが使えること
  • VSCodeをインストールしていること

各種バージョン(執筆時点)

  • Windows10 Pro(1909)
    • 2004であればHomeであってもWSL2を使ってできます
  • Docker(19.03.8)
  • Git(2.27.0)
  • VSCode(1.47.2)
  • VSCodeの拡張機能
    • Docker(任意)
    • Remote Container
    • Go(コンテナ内部で利用)
    • Vuter(コンテナ内部で利用)

(追記1)
コンテナ内部の設定やモジュールを用いて開発することができますので、ローカルにはGo言語もNodeJSもインストールしたりPathを通したりする必要がありません。

(追記2)
Remote Containerは必須ではありませんが、とても便利な拡張機能です。
下図のように、ローカルからVSCodeのRemote Containerでそのコンテナの内部のファイルシステムにアクセスできるようになり、ローカルと変わらない感覚でコンテナ内開発を行うことができます。
※作ったサンプルはdocker-composeでマウント設定しているので、コンテナ内のソースコード修正がローカルにも反映されます。

image.png
こちらの画像の出典および詳細: https://code.visualstudio.com/docs/remote/containers


実際の作業

こんな感じですすめました。

  1. ゴール設定
  2. Git管理の開始
  3. 環境構築
  4. VueのSPA作成(Vue Cliでプロジェクト立ち上げまで)
  5. GoのやまびこAPIサーバーの作成
  6. SPAとAPIの連携

ゴール設定

Must

  • Git管理できること
  • ブラウザで入力した文字列を戻すだけのやまびこアプリができること

Optional

  • 文字列をDBに登録できること
  • 過去の入力したすべての文字列をブラウザで閲覧できること

(終わらなかったので、後日やります。たぶん。)

Git管理の開始

  • Gitプロジェクトの作成
> mkdir vue-go-spa-sample
> cd vue-go-spa-sample

> git init
Initialized empty Git repository in C:/xxxxxxxxxxxx/repository/aocm/vue-go-spa-sample/.git/

> git commit --allow-empty -m "first commit"
[master (root-commit) 2df398f] first commit

環境構築

  • Vue用、Go用、MySQL用のDockerfileを作成して、docker-compose.ymlで一括管理
    • ファイルの中身については割愛
    • 基本的に枠だけ用意して、あとからポチポチメンテナンスしてこうというスタンスなDockerfileです
    • 理想はcompose upのタイミングでなるべくすませるべきです。(作業者によって開発環境が変わらないようにするために、ある程度自動で準備ができるようにするべき)
> docker ps
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                                NAMES
6491b0eb8e96        vue-go-spa-sample_client   "docker-entrypoint.s…"   9 minutes ago       Up 8 minutes        0.0.0.0:8080->8080/tcp               vgs-client
42d9f8f675b9        vue-go-spa-sample_server   "bash"                   27 minutes ago      Up 8 minutes        0.0.0.0:8000->8000/tcp               vgs-server
e8a1de1c140d        vue-go-spa-sample_db       "docker-entrypoint.s…"   27 minutes ago      Up 8 minutes        33060/tcp, 0.0.0.0:33060->3306/tcp   vgs-db

VueのSPA作成

  • RemoteContainerで立ち上げます
    ※画像のフォルダマークをクリックすると立ち上がります

image.png

前述の通り、RemoteContainerは必須ではないですが、コンテナ内部で開発(ローカルを汚さない)ためにあると便利である拡張機能です。

以降は、基本的には各コンテナの中で作業しています

Vue Cliのインストール

/usr/src/app # npm install -g @vue/cli

アプリケーションの作成
※それなりに時間がかかりますので、待っている間Goの方に着手しました。
※カスタマイズについては、好みとか目的によります。今回はRouterだけ使いたいが今後手を加えることを考えていろいろと好みで追加しています。

/usr/src/app # vue create vue-spa
Vue CLI v4.4.6
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, Linter, Unit
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: Airbnb
? Pick additional lint features: Lint on save
? Pick a unit testing solution: Jest
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
success Saved lockfile.
Done in 100.17s.
⚓  Running completion hooks...

?  Generating README.md...

?  Successfully created project my-project.
?  Get started with the following commands:

 $ cd my-project
 $ yarn serve

指示にしたがって起動してみます。

DONE  Compiled successfully in 14361ms 
App running at:
  - Local:   http://localhost:8080/ 

Dockerのポートを解放しておいてあるので、ホストのほうで http://localhost:8080/ をブラウザで開いてみて、画像のように表示されていれば成功です。
image.png

GoでAPI作成

RemoteContainerで立ち上げます。コンテナ内部にVSCodeサーバーを立ち上げてリンクします。

立ち上がり次第、コンテナ内部のVSCodeでGoの拡張機能をインストールします

image.png

インストールしたら、Ctrl + Shift + P で command palette 開いて 「Go: Install/Update tools」 します。
すべてチェックして実行したら、下記のログがでるまで待ちます。
image.png

.
.
Installing github.com/sqs/goreturns SUCCEEDED
Installing golang.org/x/lint/golint SUCCEEDED

All tools successfully installed. You are ready to Go :).

簡単なGoファイルを作って実行します。

package main

import (
    "fmt"
)

func main() {
    fmt.Println("####### start #######")
}
root@42d9f8f675b9:/go/src/github.com/aocm/vue-go-spa-sample# go run main.go 
####### start #######

問題なく動きそうなので、EchoでRestAPIサーバーを立てます。

package main

import (
    "github.com/aocm/vue-go-spa-sample/handler"
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

func main() {
    // Echoのインスタンス作る
    e := echo.New()

    // 全てのリクエストで差し込みたいミドルウェア(ログとか)はここ
    e.Use(middleware.CORS())
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    // ルーティング
    e.POST("/yamabiko", handler.YamabikoAPI())
    e.OPTIONS("/yamabiko", handler.OptionsCheck())

    // サーバー起動
    e.Start(":8000")
}
package handler

import (
    "net/http"

    "github.com/labstack/echo"
)

// YamabikoParam は /yamabiko が受けとるJSONパラメータを定義します。
type YamabikoParam struct {
    Message string `json:"message"`
}

// YamabikoAPI は /api/hello のPost時のJSONデータ生成処理を行います。
func YamabikoAPI() echo.HandlerFunc {
    return func(c echo.Context) error {
        param := new(YamabikoParam)
        if err := c.Bind(param); err != nil {
            return err
        }
        return c.JSON(http.StatusOK, map[string]interface{}{"hello": param.Message})
    }
}

SPAとAPIの連携

VSCodeの拡張機能のVuterをインストールします

登録画面を作成

Yamabiko.vueをつくっていきます。

<template>
  <div>
    <h1>This is an Yamabiko page</h1>
    <input v-model="message" placeholder="Say Yahho">
    <button @click="Send">Send</button>
    <p>Yamabiko : {{ yamabiko }}</p>
  </div>
</template>

<script>
export default {
  name: 'Yamabiko',
  data() {
    return {
      message: '',
      yamabiko: '',
    };
  },
  methods: {
    async Send() {
      const yamabiko = await this.CallYamabikoAPI().then((res) => res.json());
      this.yamabiko = yamabiko.message;
      window.alert(this.yamabiko);
    },

    async CallYamabikoAPI() {
      const url = 'http://localhost:8000/yamabiko';
      const data = {
        message: this.message,
      };
      try {
        return await window.fetch(url, {
          method: 'POST',
          headers: {
            'X-Requested-With': 'csrf', // csrf header
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(data),
        });
      } catch (e) {
        console.log(e);
        return e;
      }
    },
  },
};
</script>

image.png

image.png

Mustの「ブラウザで入力した文字列を戻すだけのやまびこアプリができること」が達成できました!

おまけ

Linux向けにビルドして、コンテナ内部で実行してみる

root@f83b97e201a6:/go/src/github.com/aocm/vue-go-spa-sample# GOOS=linux GOARCH=amd64 go build main.go 
root@f83b97e201a6:/go/src/github.com/aocm/vue-go-spa-sample# ./main

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.1.16
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:8000

フロントからも利用することができましたのでOKです。

Windows向けにビルドしてホストで実行してみる

portが被ると失敗しますので、番号を8001に変更した後ビルドしてみます

root@f83b97e201a6:/go/src/github.com/aocm/vue-go-spa-sample# GOOS=windows GOARCH=386 go build -o yamabiko.exe main.go

Windowsで実行してみるとこのように窓が立ち上がり、実際に動くことが確認できます。
image.png

(簡単にクロスコンパイルするのほんとすごい。。。)


おわりに

久しぶりにプログラム書いたのでところどころ詰まるところがありました。
この記事とプログラムの考案・コーディングに合計8時間くらいかかってしまったので、学習を習慣づける・コーディング力を高める・利用できるアセットを増やすためにもう少し軽めに、情報量を狭く深くアウトプットしつづけたいなとも思いました。

かなり改善点が多いコードなのでプロダクトに利用できる状態じゃないのも事実であり、アセット化するためにももっと時間をかけたいです。

それにしても、やりたいこと知りたいことに対して時間が圧倒的に足りないですね。
もし読者様の作業時短になれていれば幸いです。もし無駄なお時間になっていたらすみません。

間違っていること、質問、アドバイスおよび応援コメントなどコメントいただけると幸いです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsとGo言語で簡単なWebアプリを作成する

初投稿です。
過去の経験の復習かねて、書き起こしました。

概要

成果物はこちらになりますので、VueやGoを始めてみたい方への力になれれば幸いです。
https://github.com/aocm/vue-go-spa-sample

方針としては、なるべくローカルを汚さないことを最優先に考えました。
開発者ごとに環境が違うのも大変ですし、他のプロジェクト(自分の個人開発・仕事)で他のバージョンを利用していたら思わぬ影響が出てしまうのも嫌ですので、直接ローカルにインストールするのはなるべく避けたいです。

今回はDockerを使って、コンテナ内部で開発環境を作って作業していきます。
ただ、Gitについてはローカル(ホスト)でしか利用できない環境になってしまったので、そこは課題です。
(最低限、更新差分はコンテナの中でも確認できるようにしたほうが親切でした。)

対象読者

  • Dockerは最低限環境構築できているけど、Webアプリ開発ってどこから手を付ければいいんだ?という人
  • Goの簡単な動くサンプルが見たい人
  • Vueの簡単な動くサンプルが見たい人

※きれいなコードを書くことが目的ではないのでご了承ください

書くこと

  • なるべくローカルを汚さないような開発環境構築の方法
    • ローカルを汚す...ここでは、各プラグインやソフトウェアのインストールしまくる状態を指します
    • プロジェクトによってバージョンが違うなど色々障害があると思うので、それの対策です
  • 簡単なアプリ作成
    • VueのSPAサンプル
    • Go(Echo)のREST APIサーバー
  • (おまけ)クロスコンパイルしてWindows上でやまびこアプリを起動してみる

ここで書かないこと

  • 本番環境のセッティング、CI/CD、テスト
  • 本番環境を意識したリスク管理
  • 言語の基礎説明
  • Git、GitHubの基本的な使い方

※気が向いたら、時間をつくって別の記事に書くかもしれません

必要事項

  • Docker・docker-composeが使えること
  • Gitが使えること
  • VSCodeをインストールしていること

各種バージョン(執筆時点)

  • Windows10 Pro(1909)
    • 2004であればHomeであってもWSL2を使ってできます
  • Docker(19.03.8)
  • Git(2.27.0)
  • VSCode(1.47.2)
  • VSCodeの拡張機能
    • Docker
    • RemoteContainer
    • Go(コンテナ内部で利用)
    • Vuter(コンテナ内部で利用)

実際の作業

こんな感じですすめました。

  1. ゴール設定
  2. Git管理の開始
  3. 環境構築
  4. VueのSPA作成(Vue Cliでプロジェクト立ち上げまで)
  5. GoのやまびこAPIサーバーの作成
  6. SPAとAPIの連携

ゴール設定

Must

  • Git管理できること
  • ブラウザで入力した文字列を戻すだけのやまびこアプリができること

Optional

  • 文字列をDBに登録できること
  • 過去の入力したすべての文字列をブラウザで閲覧できること

(終わらなかったので、後日やります。たぶん。)

Git管理の開始

  • Gitプロジェクトの作成
> mkdir vue-go-spa-sample
> cd vue-go-spa-sample

> git init
Initialized empty Git repository in C:/xxxxxxxxxxxx/repository/aocm/vue-go-spa-sample/.git/

> git commit --allow-empty -m "first commit"
[master (root-commit) 2df398f] first commit

環境構築

  • Vue用、Go用、MySQL用のDockerfileを作成して、docker-compose.ymlで一括管理
    • ファイルの中身については割愛
    • 基本的に枠だけ用意して、あとからポチポチメンテナンスしてこうというスタンスなDockerfileです
    • 理想はcompose upのタイミングでなるべくすませるべきです。(作業者によって開発環境が変わらないようにするために、ある程度自動で準備ができるようにするべき)
> docker ps
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                                NAMES
6491b0eb8e96        vue-go-spa-sample_client   "docker-entrypoint.s…"   9 minutes ago       Up 8 minutes        0.0.0.0:8080->8080/tcp               vgs-client
42d9f8f675b9        vue-go-spa-sample_server   "bash"                   27 minutes ago      Up 8 minutes        0.0.0.0:8000->8000/tcp               vgs-server
e8a1de1c140d        vue-go-spa-sample_db       "docker-entrypoint.s…"   27 minutes ago      Up 8 minutes        33060/tcp, 0.0.0.0:33060->3306/tcp   vgs-db

VueのSPA作成

  • RemoteContainerで立ち上げます(必須ではないですが、ローカルを汚さない目的です。)
    ※画像のフォルダマークをクリックすると立ち上がります

image.png

Vue Cliのインストール

/usr/src/app # npm install -g @vue/cli

アプリケーションの作成
※それなりに時間がかかりますので、待っている間Goの方に着手しました。
※カスタマイズについては、好みとか目的によります。今回はRouterだけ使いたいが今後手を加えることを考えていろいろと好みで追加しています。

/usr/src/app # vue create vue-spa
Vue CLI v4.4.6
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, Linter, Unit
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: Airbnb
? Pick additional lint features: Lint on save
? Pick a unit testing solution: Jest
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
success Saved lockfile.
Done in 100.17s.
⚓  Running completion hooks...

?  Generating README.md...

?  Successfully created project my-project.
?  Get started with the following commands:

 $ cd my-project
 $ yarn serve

指示にしたがって起動してみます。

DONE  Compiled successfully in 14361ms 
App running at:
  - Local:   http://localhost:8080/ 

Dockerのポートを解放しておいてあるので、ホストのほうで http://localhost:8080/ をブラウザで開いてみて、画像のように表示されていれば成功です。
image.png

GoでAPI作成

RemoteContainerで立ち上げます。コンテナ内部にVSCodeサーバーを立ち上げてリンクします。
image.png
こちらの画像の出典および詳細: https://code.visualstudio.com/docs/remote/containers

以降は、基本的には各コンテナの中で作業しています

立ち上がり次第、コンテナ内部のVSCodeでGoの拡張機能をインストールします

image.png

インストールしたら、Ctrl + Shift + P で command palette 開いて 「Go: Install/Update tools」 します。
すべてチェックして実行したら、下記のログがでるまで待ちます。
image.png

.
.
Installing github.com/sqs/goreturns SUCCEEDED
Installing golang.org/x/lint/golint SUCCEEDED

All tools successfully installed. You are ready to Go :).

簡単なGoファイルを作って実行します。

package main

import (
    "fmt"
)

func main() {
    fmt.Println("####### start #######")
}
root@42d9f8f675b9:/go/src/github.com/aocm/vue-go-spa-sample# go run main.go 
####### start #######

問題なく動きそうなので、EchoでRestAPIサーバーを立てます。

package main

import (
    "github.com/aocm/vue-go-spa-sample/handler"
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

func main() {
    // Echoのインスタンス作る
    e := echo.New()

    // 全てのリクエストで差し込みたいミドルウェア(ログとか)はここ
    e.Use(middleware.CORS())
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    // ルーティング
    e.POST("/yamabiko", handler.YamabikoAPI())
    e.OPTIONS("/yamabiko", handler.OptionsCheck())

    // サーバー起動
    e.Start(":8000")
}
package handler

import (
    "net/http"

    "github.com/labstack/echo"
)

// YamabikoParam は /yamabiko が受けとるJSONパラメータを定義します。
type YamabikoParam struct {
    Message string `json:"message"`
}

// YamabikoAPI は /api/hello のPost時のJSONデータ生成処理を行います。
func YamabikoAPI() echo.HandlerFunc {
    return func(c echo.Context) error {
        param := new(YamabikoParam)
        if err := c.Bind(param); err != nil {
            return err
        }
        return c.JSON(http.StatusOK, map[string]interface{}{"hello": param.Message})
    }
}

SPAとAPIの連携

VSCodeの拡張機能のVuterをインストールします

登録画面を作成

Yamabiko.vueをつくっていきます。

<template>
  <div>
    <h1>This is an Yamabiko page</h1>
    <input v-model="message" placeholder="Say Yahho">
    <button @click="Send">Send</button>
    <p>Yamabiko : {{ yamabiko }}</p>
  </div>
</template>

<script>
export default {
  name: 'Yamabiko',
  data() {
    return {
      message: '',
      yamabiko: '',
    };
  },
  methods: {
    async Send() {
      const yamabiko = await this.CallYamabikoAPI().then((res) => res.json());
      this.yamabiko = yamabiko.message;
      window.alert(this.yamabiko);
    },

    async CallYamabikoAPI() {
      const url = 'http://localhost:8000/yamabiko';
      const data = {
        message: this.message,
      };
      try {
        return await window.fetch(url, {
          method: 'POST',
          headers: {
            'X-Requested-With': 'csrf', // csrf header
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(data),
        });
      } catch (e) {
        console.log(e);
        return e;
      }
    },
  },
};
</script>

image.png

image.png

Mustの「ブラウザで入力した文字列を戻すだけのやまびこアプリができること」が達成できました!

おまけ

Linux向けにビルドして、コンテナ内部で実行してみる

root@f83b97e201a6:/go/src/github.com/aocm/vue-go-spa-sample# GOOS=linux GOARCH=amd64 go build main.go 
root@f83b97e201a6:/go/src/github.com/aocm/vue-go-spa-sample# ./main

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.1.16
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:8000

フロントからも利用することができましたのでOKです。

Windows向けにビルドしてホストで実行してみる

portが被ると失敗しますので、番号を8001に変更した後ビルドしてみます

root@f83b97e201a6:/go/src/github.com/aocm/vue-go-spa-sample# GOOS=windows GOARCH=386 go build -o yamabiko.exe main.go

Windowsで実行してみるとこのように窓が立ち上がり、実際に動くことが確認できます。
image.png

(簡単にクロスコンパイルするのほんとすごい。。。)


おわりに

久しぶりにプログラム書いたのでところどころ詰まるところがありました。
この記事とプログラムの考案・コーディングに合計8時間くらいかかってしまったので、学習を習慣づける・コーディング力を高める・利用できるアセットを増やすためにもう少し軽めに、情報量を狭く深くアウトプットしつづけたいなとも思いました。

かなり改善点が多いコードなのでプロダクトに利用できる状態じゃないのも事実であり、アセット化するためにももっと時間をかけたいです。

それにしても、やりたいこと知りたいことに対して時間が圧倒的に足りないですね。
もし読者様の作業時短になれていれば幸いです。もし無駄なお時間になっていたらすみません。

間違っていること、質問、アドバイスおよび応援コメントなどコメントいただけると幸いです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsとGo言語で簡単なWebアプリを作成する(Dockerで開発環境構築)

初投稿です。
過去の経験の復習かねて、書き起こしました。

概要

成果物はこちらになりますので、VueやGoを始めてみたい方への力になれれば幸いです。
https://github.com/aocm/vue-go-spa-sample

方針としては、なるべくローカルを汚さないことを最優先に考えました。
開発者ごとに環境が違うのも大変ですし、他のプロジェクト(自分の個人開発・仕事)で他のバージョンを利用していたら思わぬ影響が出てしまうのも嫌ですので、直接ローカルにインストールするのはなるべく避けたいです。

今回はDockerを使って、コンテナ内部で開発環境を作って作業していきます。
ただ、Gitについてはローカル(ホスト)でしか利用できない環境になってしまったので、そこは課題です。
(最低限、更新差分はコンテナの中でも確認できるようにしたほうが親切でした。)

対象読者

  • Dockerは最低限環境構築できているけど、Webアプリ開発ってどこから手を付ければいいんだ?という人
  • Dockerで開発環境を構築するサンプルが見たい人
  • Goの簡単な動くサンプルが見たい人
  • Vueの簡単な動くサンプルが見たい人

※きれいなコードを書くことが目的ではないのでご了承ください

書くこと

  • なるべくローカルを汚さないような開発環境構築の方法
    • ローカルを汚す...ここでは、各プラグインやソフトウェアをローカルにインストールしまくる状態を指します
    • プロジェクトによってバージョンが違うなど色々障害があると思うので、それの対策です
  • 簡単なアプリ作成
    • VueのSPAサンプル
    • Go(Echo)のREST APIサーバー
  • (おまけ)クロスコンパイルしてWindows上でやまびこアプリを起動してみる

ここで書かないこと

  • 本番環境のセッティング、CI/CD、テスト
  • 本番環境を意識したリスク管理
  • 言語の基礎説明
  • Git、GitHubの基本的な使い方

※気が向いたら、時間をつくって別の記事に書くかもしれません

必要事項

  • Docker・docker-composeが使えること
  • Gitが使えること
  • VSCodeをインストールしていること

各種バージョン(執筆時点)

  • Windows10 Pro(1909)
    • 2004であればHomeであってもWSL2を使ってできます
  • Docker(19.03.8)
  • Git(2.27.0)
  • VSCode(1.47.2)
  • VSCodeの拡張機能
    • Docker(任意)
    • Remote Container
    • Go(コンテナ内部で利用)
    • Vuter(コンテナ内部で利用)

(追記1)
コンテナ内部の設定やモジュールを用いて開発することができますので、ローカルにはGo言語もNodeJSもインストールしたりPathを通したりする必要がありません。

(追記2)
Remote Containerは必須ではありませんが、とても便利な拡張機能です。
下図のように、ローカルからVSCodeのRemote Containerでそのコンテナの内部のファイルシステムにアクセスできるようになり、ローカルと変わらない感覚でコンテナ内開発を行うことができます。
※作ったサンプルはdocker-composeでマウント設定しているので、コンテナ内のソースコード修正がローカルにも反映されます。

image.png
こちらの画像の出典および詳細: https://code.visualstudio.com/docs/remote/containers


実際の作業

こんな感じですすめました。

  1. ゴール設定
  2. Git管理の開始
  3. 環境構築
  4. VueのSPA作成(Vue Cliでプロジェクト立ち上げまで)
  5. GoのやまびこAPIサーバーの作成
  6. SPAとAPIの連携

ゴール設定

Must

  • Git管理できること
  • ブラウザで入力した文字列を戻すだけのやまびこアプリができること

Optional

  • 文字列をDBに登録できること
  • 過去の入力したすべての文字列をブラウザで閲覧できること

(終わらなかったので、後日やります。たぶん。)

Git管理の開始

  • Gitプロジェクトの作成
> mkdir vue-go-spa-sample
> cd vue-go-spa-sample

> git init
Initialized empty Git repository in C:/xxxxxxxxxxxx/repository/aocm/vue-go-spa-sample/.git/

> git commit --allow-empty -m "first commit"
[master (root-commit) 2df398f] first commit

環境構築

  • Vue用、Go用、MySQL用のDockerfileを作成して、docker-compose.ymlで一括管理
    • ファイルの中身については割愛
    • 基本的に枠だけ用意して、あとからポチポチメンテナンスしてこうというスタンスなDockerfileです
    • 理想はcompose upのタイミングでなるべくすませるべきです。(作業者によって開発環境が変わらないようにするために、ある程度自動で準備ができるようにするべき)
> docker ps
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                                NAMES
6491b0eb8e96        vue-go-spa-sample_client   "docker-entrypoint.s…"   9 minutes ago       Up 8 minutes        0.0.0.0:8080->8080/tcp               vgs-client
42d9f8f675b9        vue-go-spa-sample_server   "bash"                   27 minutes ago      Up 8 minutes        0.0.0.0:8000->8000/tcp               vgs-server
e8a1de1c140d        vue-go-spa-sample_db       "docker-entrypoint.s…"   27 minutes ago      Up 8 minutes        33060/tcp, 0.0.0.0:33060->3306/tcp   vgs-db

VueのSPA作成

  • RemoteContainerで立ち上げます
    ※画像のフォルダマークをクリックすると立ち上がります

image.png

前述の通り、RemoteContainerは必須ではないですが、コンテナ内部で開発(ローカルを汚さない)ためにあると便利である拡張機能です。

以降は、基本的には各コンテナの中で作業しています

Vue Cliのインストール

/usr/src/app # npm install -g @vue/cli

アプリケーションの作成
※それなりに時間がかかりますので、待っている間Goの方に着手しました。
※カスタマイズについては、好みとか目的によります。今回はRouterだけ使いたいが今後手を加えることを考えていろいろと好みで追加しています。

/usr/src/app # vue create vue-spa
Vue CLI v4.4.6
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, Linter, Unit
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: Airbnb
? Pick additional lint features: Lint on save
? Pick a unit testing solution: Jest
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
success Saved lockfile.
Done in 100.17s.
⚓  Running completion hooks...

?  Generating README.md...

?  Successfully created project my-project.
?  Get started with the following commands:

 $ cd my-project
 $ yarn serve

指示にしたがって起動してみます。

DONE  Compiled successfully in 14361ms 
App running at:
  - Local:   http://localhost:8080/ 

Dockerのポートを解放しておいてあるので、ホストのほうで http://localhost:8080/ をブラウザで開いてみて、画像のように表示されていれば成功です。
image.png

GoでAPI作成

RemoteContainerで立ち上げます。コンテナ内部にVSCodeサーバーを立ち上げてリンクします。

立ち上がり次第、コンテナ内部のVSCodeでGoの拡張機能をインストールします

image.png

インストールしたら、Ctrl + Shift + P で command palette 開いて 「Go: Install/Update tools」 します。
すべてチェックして実行したら、下記のログがでるまで待ちます。
image.png

.
.
Installing github.com/sqs/goreturns SUCCEEDED
Installing golang.org/x/lint/golint SUCCEEDED

All tools successfully installed. You are ready to Go :).

簡単なGoファイルを作って実行します。

package main

import (
    "fmt"
)

func main() {
    fmt.Println("####### start #######")
}
root@42d9f8f675b9:/go/src/github.com/aocm/vue-go-spa-sample# go run main.go 
####### start #######

問題なく動きそうなので、EchoでRestAPIサーバーを立てます。

package main

import (
    "github.com/aocm/vue-go-spa-sample/handler"
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

func main() {
    // Echoのインスタンス作る
    e := echo.New()

    // 全てのリクエストで差し込みたいミドルウェア(ログとか)はここ
    e.Use(middleware.CORS())
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    // ルーティング
    e.POST("/yamabiko", handler.YamabikoAPI())
    e.OPTIONS("/yamabiko", handler.OptionsCheck())

    // サーバー起動
    e.Start(":8000")
}
package handler

import (
    "net/http"

    "github.com/labstack/echo"
)

// YamabikoParam は /yamabiko が受けとるJSONパラメータを定義します。
type YamabikoParam struct {
    Message string `json:"message"`
}

// YamabikoAPI は /api/hello のPost時のJSONデータ生成処理を行います。
func YamabikoAPI() echo.HandlerFunc {
    return func(c echo.Context) error {
        param := new(YamabikoParam)
        if err := c.Bind(param); err != nil {
            return err
        }
        return c.JSON(http.StatusOK, map[string]interface{}{"hello": param.Message})
    }
}

SPAとAPIの連携

VSCodeの拡張機能のVuterをインストールします

登録画面を作成

Yamabiko.vueをつくっていきます。

<template>
  <div>
    <h1>This is an Yamabiko page</h1>
    <input v-model="message" placeholder="Say Yahho">
    <button @click="Send">Send</button>
    <p>Yamabiko : {{ yamabiko }}</p>
  </div>
</template>

<script>
export default {
  name: 'Yamabiko',
  data() {
    return {
      message: '',
      yamabiko: '',
    };
  },
  methods: {
    async Send() {
      const yamabiko = await this.CallYamabikoAPI().then((res) => res.json());
      this.yamabiko = yamabiko.message;
      window.alert(this.yamabiko);
    },

    async CallYamabikoAPI() {
      const url = 'http://localhost:8000/yamabiko';
      const data = {
        message: this.message,
      };
      try {
        return await window.fetch(url, {
          method: 'POST',
          headers: {
            'X-Requested-With': 'csrf', // csrf header
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(data),
        });
      } catch (e) {
        console.log(e);
        return e;
      }
    },
  },
};
</script>

image.png

image.png

Mustの「ブラウザで入力した文字列を戻すだけのやまびこアプリができること」が達成できました!

おまけ

Linux向けにビルドして、コンテナ内部で実行してみる

root@f83b97e201a6:/go/src/github.com/aocm/vue-go-spa-sample# GOOS=linux GOARCH=amd64 go build main.go 
root@f83b97e201a6:/go/src/github.com/aocm/vue-go-spa-sample# ./main

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.1.16
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:8000

フロントからも利用することができましたのでOKです。

Windows向けにビルドしてホストで実行してみる

portが被ると失敗しますので、番号を8001に変更した後ビルドしてみます

root@f83b97e201a6:/go/src/github.com/aocm/vue-go-spa-sample# GOOS=windows GOARCH=386 go build -o yamabiko.exe main.go

Windowsで実行してみるとこのように窓が立ち上がり、実際に動くことが確認できます。
image.png

(簡単にクロスコンパイルするのほんとすごい。。。)


おわりに

久しぶりにプログラム書いたのでところどころ詰まるところがありました。
この記事とプログラムの考案・コーディングに合計8時間くらいかかってしまったので、学習を習慣づける・コーディング力を高める・利用できるアセットを増やすためにもう少し軽めに、情報量を狭く深くアウトプットしつづけたいなとも思いました。

かなり改善点が多いコードなのでプロダクトに利用できる状態じゃないのも事実であり、アセット化するためにももっと時間をかけたいです。

それにしても、やりたいこと知りたいことに対して時間が圧倒的に足りないですね。
もし読者様の作業時短になれていれば幸いです。もし無駄なお時間になっていたらすみません。

間違っていること、質問、アドバイスおよび応援コメントなどコメントいただけると幸いです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Goで株価をスクレイピングしてcsvファイルに出力するまで

1. 株価などのデータを提供しているサイトからまずはドキュメントをGetで取得してきて
2.そこから必要なデータを取り出して
3. 取り出したデータをcsvファイルに吐き出す

ってところまでを、Goで書いた記事です。
自分の整理用の記事ですが、誰かの参考になれば嬉しいです。

Goは普段書かないので、もしもっといい書き方とかあればコメントいただけると助かります。:rolling_eyes:

準備

  • ライブラリのインポート

    • goqueryをインポート (GoでjQuery的にスクレイピングやクローリングができるライブラリです)
  • スクレイピングするサイトの確認

    • Goに限らず当然ですが、スクレイピングをしてよいサイトかどうかは、事前にしっかり確認(各サイトのrobots.txtを見ることでその確認ができます)

調べた結果、ありがたいことにこちらのサイトで株価のデータを提供してくださっているので、こちらを今回は利用させていただいています。
:moneybag:株式投資メモ 株価DB:moneybag:

今回の目的は、上記のサイトにスクレイピングを行って、その日の出来高上位100位でかつ終値が2000円以下の銘柄の1年分の株データを取得して、csvファイルの形にすることです。

Getリクエスト

まずは、NewDocumentで対象のURLからドキュメントを取得するところ。
goquery.NewDocument()の中に対象URLを書きます。
今回は、URLの中に日付が含まれるので、timeパッケージを使用して現在の日付を指定するようにしております。

day := time.Now()
doc, err := goquery.NewDocument(fmt.Sprintf("https://kabuoji3.com/ranking/?date=%s&type=3&market=1", day.Format(layout)))
    if err != nil {
        log.Fatalln(err)
    }
 log.Println(doc)

これで出来高ランキングの上位100位の銘柄のデータを取得できました。:smile:

取得したデータの操作

次に、取得したデータすべてのうち、条件を満たす株の銘柄コードの配列を生成するように操作を加えます。

対象ページのHTMLの構成を調べると、テーブルになっており、trタグの中のthタグにテーブルのヘッドがあり、trタグの中のtdタグにデータが入っている構成でした。

以下の流れで銘柄を絞り込みます。

  1. 銘柄コードと株価(終値)をマップに
  2. 株価(終値)が2000円以下だけの銘柄コードを配列に
tr := doc.Find("tr")
    var codeName string
    var value float64
    codeMap := make(map[string]float64)

    // 1. 銘柄コードと株価(終値)をmapに
    tr.Each(func(index int, s *goquery.Selection) {
        s.Find("td").Each(func(index int, s *goquery.Selection) {
            switch index {
            case 1:
                codeName = s.Text()[:4]
            case 3:
                value, _ = strconv.ParseFloat(s.Text(), 32)
            }
        })
        codeMap[codeName] = value
    })

    // 2. 株価(終値)が2000円以下だけの銘柄コードを配列に
    var data []string
    for key, value := range codeMap {
        if value < limitValue {
            data = append(data, key)
        }
    }

これで、その日の出来高上位100位でかつ終値が2000円以下の銘柄コードの配列を生成できました。:relaxed:

各銘柄の1年分の株データを取得

取得したい銘柄の株価データは、"http://・・・・/銘柄コード/年”
という形式になっていたので、こんな感じ銘柄コードと年を指定して取得します。

doc, err := goquery.NewDocument(fmt.Sprintf("http://kabuoji3.com/stock/%s/%d/", stockCode, year))
    if err != nil {
        log.Fatalln(err)
    }

あとはこれを銘柄コードの配列の長さだけfor文で繰り返して取得すればよいのですが、スクレイピングさせていただいているサイトへの負荷を配慮してスリープを忘れずに入れます。:sleeping:

time.Sleep(time.Second * 1)

これで1秒に1回リクエストが送られようになります。

csvファイルに出力

最後に取得した各銘柄のデータを以下のようなテーブルをもつ
csvファイルに書き出すよう処理を追加します。

date open high low close volume closing_adustment
2020-01-01 xxx xxx xxx xxx xxx xxx
2020-01-02 xxx xxx xxx xxx xxx xxx
2020-01-03 xxx xxx xxx xxx xxx xxx
xxx xxx xxx xxx xxx xxx

スクレイピングして取得したcsvファイルを分析などで使用することを考えたとき、ファイルに日本語文字を入れたくなかったので、ヘッダーとなる部分を意図的に英語にしています。
open:始値, high:高値, low:安値, close:終値, volume:出来高, closing_adustment:終値調整
(英訳これでいいはず)

// O_WRONLY:書き込みモード開く, O_CREATE:無かったらファイルを作成
file, err := os.OpenFile(fmt.Sprintf("./crawlData/%s.csv", stockCode), os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
    log.Println("Error:", err)
}
defer file.Close()
err = file.Truncate(0) // ファイルを空っぽにする(2回目以降用)

writer := csv.NewWriter(file)
var data []string
doc.Find("tr").Each(func(index int, s *goquery.Selection) {
    if index == 0 {
        data = []string{"date", "open", "high", "low", "close", "volume", "closing_adustment"}
        writer.Write(data)
    } else {
        s.Find("td").Each(func(index int, s *goquery.Selection) {
            if index == 0 {
                t, _ := time.Parse(layout, s.Text())
                data = append(data, t.Format(layout))
            } else {
                data = append(data, s.Text())
            }
        })
        writer.Write(data)
    }
    data = nil
})
writer.Flush()
}

ちなみに、もし日本語入力に対応したければ、writerのところを以下のようにすれば文字化けせずにファイル出力できます。

// ShiftJISのエンコーダーを噛ませたWriterを作成する
writer := transform.NewWriter(sjisFile, japanese.ShiftJIS.NewEncoder())

全体のサンプルコード

最後に全体のコードを貼っておきます。

sample.go
package main

import (
    "encoding/csv"
    "fmt"
    "log"
    "os"
    "strconv"
    "time"

    "github.com/PuerkitoBio/goquery"
)

const limitValue = 2000
const layout = "2006-01-02"

func main() {
    saveStockDataFile(getStockCodeList(limitValue))
}

func getStockCodeList(limitValue float64) []string {
    day := time.Now()
    doc, err := goquery.NewDocument(fmt.Sprintf("https://kabuoji3.com/ranking/?date=%s&type=3&market=1", day.Format(layout)))
    if err != nil {
        log.Fatalln(err)
    }

    tr := doc.Find("tr")
    var codeName string
    var value float64
    codeMap := make(map[string]float64)

    // 1. 銘柄コードと株価(終値)をmapに
    tr.Each(func(index int, s *goquery.Selection) {
        s.Find("td").Each(func(index int, s *goquery.Selection) {
            switch index {
            case 1:
                codeName = s.Text()[:4]
            case 3:
                value, _ = strconv.ParseFloat(s.Text(), 32)
            }
        })
        if codeName != "" {
            codeMap[codeName] = value
        }
    })

    // 2. 株価(終値)が2000円以下だけの銘柄コードを配列に
    var data []string
    for key, value := range codeMap {
        if value < limitValue {
            data = append(data, key)
        }
    }
    return data
}

func saveStockDataFile(codeList []string) {
    for _, stockCode := range codeList {
        doc := crawlStockData(stockCode, time.Now().Year())
        fmt.Println(doc.Find("title").Text())

        // O_WRONLY:書き込みモード開く, O_CREATE:無かったらファイルを作成
        file, err := os.OpenFile(fmt.Sprintf("./crawlData/%s.csv", stockCode), os.O_WRONLY|os.O_CREATE, 0600)
        if err != nil {
            log.Println("Error:", err)
        }
        defer file.Close()
        err = file.Truncate(0) // ファイルを空っぽにする(2回目以降用)

        writer := csv.NewWriter(file)
        var data []string
        doc.Find("tr").Each(func(index int, s *goquery.Selection) {
            if index == 0 {
                data = []string{"date", "open", "high", "low", "close", "volume", "closing_adustment"}
                writer.Write(data)
            } else {
                s.Find("td").Each(func(index int, s *goquery.Selection) {
                    if index == 0 {
                        t, _ := time.Parse(layout, s.Text())
                        data = append(data, t.Format(layout))
                    } else {
                        data = append(data, s.Text())
                    }
                })
                writer.Write(data)
            }
            data = nil
        })
        writer.Flush()
        time.Sleep(time.Second * 1)
    }
}

func crawlStockData(stockCode string, year int) *goquery.Document {
    doc, err := goquery.NewDocument(fmt.Sprintf("http://kabuoji3.com/stock/%s/%d/", stockCode, year))
    if err != nil {
        log.Fatalln(err)
    }
    return doc
}

やりたかったことは完了です。:sparkles:

goqueryには他にも色々便利なメソッドがあるので、Goでスクレイピングをサクッと書きたい場合はとても便利そうでした!

UTをこれから書く予定なので、記事は随時更新するかと思います。
最後まで読んでいただき、ありがとうございました。:bow::dog2:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AzureとSDKとGoと私

2020年7月8日(水)にオンラインで開催されたMicrosoft Open Tech Night主催の

にて「AzureとSDKとGoと私」というタイトルで発表をさせて頂きました。

thumbnail

GoやAzure SDK for Goは初めてという方向けに、

  • Goの概要
  • AzureとSDKの構成(RESTful、autorest、など)
  • Azure SDK for Goについて
  • 私の活動内容
  • Azure Functions for Go

について紹介しています。

興味のある方は一読して頂けると、Azure SDK for Goの扉の前にお連れできると思います。

P.S. 日本マイクロソフト公式Twitterアカウント「Microsoft Tech」(@msdevjp)さんに「Go」の二文字がツイートされる奇跡!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GoでEC2describeinstanceをする

  • リージョンを指定しないとだめだった(awsのdefaultのcredentialにセットされていても)
  • DescribeInstancesはerrにあたる部分のエラーハンドリング用の引数を指定しないと怒られた(そういうものなんですか)
    • ./describe.go:32:33: multiple-value svc.DescribeInstances() in single-value context
package main

import (
    "fmt"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/awserr"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/ec2"
)

func main() {
    svc := ec2.New(
        session.New(),
        &aws.Config{
            Region: aws.String("ap-northeast-1"),
        })
    input := &ec2.DescribeInstancesInput{
        InstanceIds: []*string{
            aws.String("i-xxxxxxxxxxxxxxxx"),
        },
    }

    result, err := svc.DescribeInstances(input)
    if err != nil {
        if aerr, ok := err.(awserr.Error); ok {
            switch aerr.Code() {
            default:
                fmt.Println(aerr.Error())
            }
        } else {
            // Print the error, cast err to awserr.Error to get the Code and
            // Message from an error.
            fmt.Println(err.Error())
        }
        return
    }
    fmt.Println(result)
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む