Golang で Web API を扱う方法

概要

  • Golang を使って、Web API から取ってきた JSON を構造体に代入する方法
  • 総務省の無線局情報取得APIを利用する

コード

総務省の無線局等情報検索APIについては、Web-API機能(無線局等情報検索)についてをご覧ください。

総務省の無線局等情報検索APIは、https://www.tele.soumu.go.jp/musen/list?ST=1&OF=2&OW=AT&DA=1&SC=1&DC=1のようなリクエストを送ると、下記のようなJSONを取得できます。 この場合は、アマチュア無線局について検索結果の1件目から500件分の詳細情報をJSON形式で送ってくださいという意味のリクエストになります。

{
    "musen": [{
        "detailInfo": {
            "radioSpec1": "A1A\\t435.795 MHz\\t1   W\\nG1D\\t435.91 MHz\\t1   W\\n20K0F1D\\t435.91 MHz\\t1   W\\n20K0F3E\\t435.91 MHz\\t1   W\\nA1A\\t435.795 MHz から        435.9 MHz まで\\t1   W\\nJ3E\\t435.795 MHz から        435.9 MHz まで\\t1   W",
            "radioStationPurpose": "アマチュア業務用",
            "note": "",
            "permittedOperatingHours": "常 時",
            "address": "*****",
            "licenseDate": "2016-08-19",
            "broadcastMatter": "",
            "commMatter": "アマチュア業務に関する事項",
            "office": "",
            "validTerms": "2021-08-18まで",
            "commPartner": "アマチュア局",
            "startingLimit": "",
            "broadcastDistrict": "",
            "workPersonName": "",
            "identificationSignals": "8J1JCS      ",
            "radioStationNumber": "",
            "radioStationCategory": "アマチュア局",
            "movementArea": "",
            "radioEuipmentLocation": "軌道傾斜角 98度\\n軌道周期  110分\\n遠地点高度 1,705km\\n近地点高度 805km\\n軌道の種類 円軌道極軌道",
            "licenseNumber": "*****",
            "name": "一般社団法人日本アマチュア無線連盟"
        },
        "listInfo": {
            "name": "一般社団法人日本アマチュア無線連盟(8J1JCS)",
            "radioStationPurpose": "アマチュア業務用",
            "tdfkCd": " ",
            "no": "1",
            "licenseDate": "2016-08-19"
        }
    }, {
            ...
            ...
            省略
            ...
            ...
        }
    }],
    "musenInformation": {
        "totalCount": "408919",
        "lastUpdateDate": "2019-07-05"
    }
}

これをJSON-to-Go を使って、Golangの構造体に変換します。 生成直後は、構造体の名前が AutoGenerated となっているので、適当な名前に変更します。 ここでは、無線局詳細情報のリストを表しているので、radioListsとしました。

// json struct
// generated by this site:  
type radioLists struct {
    Musen []struct {
        DetailInfo struct {
            RadioSpec1              string `json:"radioSpec1"`
            ...                     ...
            省略
            ...                     ...
        } `json:"detailInfo"`
        ListInfo struct {
            Name                string `json:"name"`
            RadioStationPurpose string `json:"radioStationPurpose"`
            TdfkCd              string `json:"tdfkCd"`
            No                  string `json:"no"`
            LicenseDate         string `json:"licenseDate"`
        } `json:"listInfo"`
    } `json:"musen"`
    MusenInformation struct {
        TotalCount     string `json:"totalCount"`
        LastUpdateDate string `json:"lastUpdateDate"`
    } `json:"musenInformation"`
}

次に、APIにリクエストを投げて、構造体を返してくれる関数を書きます。

// get list of radio
// sc is start count
// sc番目から最大500件のデータを取得します。
func requestList(sc int) (*radioLists, error) {
    // Options
    st := 1    // 免許状の情報
    of := 2    // json format
    ow := "AT" // amateur radio
    da := 1    // request details
    dc := 2    // Number of acquisitions
    url := fmt.Sprintf("https://www.tele.soumu.go.jp/musen/list?ST=%d&OF=%d&OW=%s&DA=%d&SC=%d&DC=%d", st, of, ow, da, sc, dc)

    // get web api response
    resp, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    // check HTTP status code
    if 300 <= resp.StatusCode && resp.StatusCode < 200 {
        return nil, errors.New("request faild")
    }

    // decode response data 
    var data *radioLists
    err = json.NewDecoder(resp.Body).Decode(&data)
    if err != nil {
        return nil, err
    }

    return data, nil
}
url := fmt.Sprintf("https://www.tele.soumu.go.jp/musen/list?ST=%d&OF=%d&OW=%s&DA=%d&SC=%d&DC=%d", st, of, ow, da, sc, dc)

初めに、fmt.Sprintf()を使い、リクエストを組み立てurlに代入します。 オプションを後々変更することを考えて、変数に一度代入してから、fmt.Sprintfで結合しています。 そのURLを使用して、http.Get()でレスポンスを得ます。 実際には、HTTPのステータスコードをチェックして、対応した処理を行う必要があるのですが、 簡単にするために、200番台なら続行、それ以外の場合はすべてエラーとしています。

得られたレスポンスボディを下記のコードでパースして終了! めちゃくちゃ簡単。

// decode response data by json format
var data *radioLists
err = json.NewDecoder(resp.Body).Decode(&data)
if err != nil {
    return nil, err
}

このrequestList関数は、引数に何件目から取得するかを指定して呼び出します。 下のような感じで使います。

lists, err := requestList(1)
if err != nil {
    fmt.Println(err)
}

あとは、得られたJSONを表す構造体を使えばいろいろと遊べます。 すべての無線局の情報について、取得したい場合は、検索結果の総件数を超えるまで requestList() を引数を変えながら呼び出すことでできます。 総件数は、requestList() で得られる構造体に含まれているのでそれを利用します。

ただし、その際はサーバに負荷を掛けすぎないように行ってください。