こんにちは、新卒のmako_makokです。 今回はGo未経験者が外部のAPIを叩いてみた話になります。
はじめに
自分でカスタマイズしたクエリを使用して勉強会情報を取得したいと考えました。
普段はこういったツールは個人的にPythonで書くことが多いのですが、ある程度使い慣れた言語でリクエストを投げてパースするだけだとすぐに実装が終わってしまうので、
勉強がてら興味があったGoで実装してみました。
本日はGoで
1. connpass APIを叩き
2. レスポンスを構造体に詰め
3. 出力
するまでの手順や使用したツールについてお話ししようと思います。
この記事でやること
型を作る
今回は以下のような型を定義しました。
package types import ( "time" ) type RequestParam struct { EventID int Keyword []string KeywordOR []string YM []int YMD []int NickName []string OwnerNickname []string SeriesID int Start int Order int Count int Format string } type ResultSet struct { ResultsReturned int `json:"results_returned"` Events []Event `json:"events"` ResultsStart int `json:"results_start"` ResultsAvailable int `json:"results_available"` } type Event struct { EventURL string `json:"event_url"` EventTypes string `json:"event_type"` OwnerNickname string `json:"owner_nickname"` Series Series `json:"series"` UpdatedAt time.Time `json:"updated_at"` Lat string `json:"lat"` StartedAt time.Time `json:"started_at"` HashTag string `json:"hash_tag"` Title string `json:"title"` EventID int `json:"event_id"` Lon string `json:"lon"` Waiting int `json:"waiting"` Limit int `json:"limit"` OwnerID int `json:"owner_id"` OwnerDisplayName string `json:"owner_display_name"` Description string `json:"description"` Address string `json:"address"` Catch string `json:"catch"` Accepted int `json:"accepted"` EndedAt time.Time `json:"ended_at"` Place string `json:"place"` } type Series struct { URL string `json:"url"` ID int `json:"id"` Title string `json:"title"` }
レスポンスの型を書くのは単純作業ですし、タイプ量も少なくないので地味に面倒な作業ですが、Goの場合以下のツールで爆速で書くことができました。
JSON-to-Go: Convert JSON to Go instantly
jsonを対応したstructとタグを作成してくれます。 今回の場合ですと、
https://connpass.com/api/v1/event/?keyword=python | jq'.'
などして、整形したjsonをコピペするだけでtypeを生成してくれます。
リクエスト
ちょっとしたGETをするのであればhttp.Getで良いですが、今回は勉強のためClientの設定なども行います。
標準ライブラリのnet/http
を使用します。
package httpwrapper import ( "io" "io/ioutil" "net/http" "net/url" "time" ) func DoRequest(method, path string, values url.Values, body io.Reader) ([]byte, error) { client := &http.Client { Timeout: 20 * time.Second, } req, err := http.NewRequest(method, path, body) if err != nil { return nil, err } req.URL.RawQuery = values.Encode(); resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() data, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } return data, nil }
標準ライブラリしか使っていませんが、すごくすっきり書けて気持ちがいいです。 順番に説明していきます。すっきりしすぎてあまり説明することが無いような気がしますが、それもGolangの魅力かなと思いました。
- タイムアウト値の設定
client := &http.Client {
Timeout: 20 * time.Second,
}
タイムアウト値を20秒に設定します。
- リクエストの生成
req, err := http.NewRequest(method, path, body)
コンストラクタを使用してRequestオブジェクトを生成しています。
- パラメータの設定
req.URL.RawQuery = values.Encode();
values
はmap型です。パラメータと値がマッピングされており、 Values.Encode()でURLエンコードを行います。
- リクエストの実行
resp, err := client.Do(req)
(略)
defer resp.Body.Close()
defer
を使用すると遅延実行することができます。
今回の場合ですと、DoRequest()が実行され終了する間際にresp.Body.Close()
が走り、クローズ処理が行われます。
- 読み書きできる形にする
data, err := ioutil.ReadAll(resp.Body)
ioutil.ReadAll
でレスポンスボディをバイトのスライスにして返します。
connpass APIを叩いてみる
package main import ( "fmt" "encoding/json" "log" "strconv" "net/url" "./httpwrapper" "./types" ) func main() { const endpoint = "https://connpass.com/api/v1/event/" keywordor := []string{"JavaScript", "TypeScript", "Go", "Java", "Kotlin", "Vue", "React", "Nuxt", "Next", "Spring Boot"} keyword := []string{"東京"} count := 30 param := types.RequestParam{Keyword: keyword, KeywordOR: keywordor, Count: count} values := url.Values{"keyword": keyword, "keywordor": param.KeywordOR, "count": {strconv.Itoa(count)}} data, err := httpwrapper.DoRequest("GET", endpoint, values, nil) if err != nil { log.Fatal(err) } var rs types.ResultSet if err := json.Unmarshal(data, &rs); err != nil { log.Fatal(err) } for i, event := range rs.Events { fmt.Println(strconv.Itoa(i + 1) + " Title: " + event.Series.Title) } }
リクエストを実行します。
結果を取得できた後はencoding/json
のjson.Unmarshalメソッドでパースできます。
var rs types.ResultSet if err := json.Unmarshal(data, &rs); err != nil { log.Fatal(err) }
だいたいのjsonがこれでパースできます。*1
なお、構造体をJsonにパースする場合はjson.Marshalを使用します。
実行結果
最後に
今回はGoでconnpass APIを叩いてみました。
今後はリクエストの送信だけではなく、 RESTなAPIの作成したり、テスト等を書いて拡充していきたいと思います。