- LegiNoteプロジェクト開発物語2 - 技術スタックとワーカー
- LegiNoteプロジェクト開発物語の2番目のパートでは、Go言語を使用したワーカー開発について取り上げます。データ収集および更新ロジックの実装と、プロジェクト構成方法を紹介します。
AIが翻訳した投稿です。
durumis AIが要約した投稿
- LegiNoteプロジェクト開発中に、国会議事録OpenAPIを活用して法律案情報を取得する作業を行いました。
- Go言語を使用してHTTPリクエストでデータを取得し、受け取ったJSONデータを構造体でパースする処理を行いました。
- API認証キーの発行からデータリクエスト、パースまでの過程を説明し、今後の改善方向を探っています
こんにちは、StatPanです。
LegiNoteのサイドプロジェクトの開発記を書いています。
以前の記事は、以下のリンクをご参照ください。
ワーカー1号 worker-bill
billという名前、なんだか外国人の友達の名前みたいで親しみやすいのですが、次の単語の意味からヒントを得ました。
適切な変数名、レポ名を見つけるために、以前は国会ドメインに関連するあれこれの英語単語を探していたのですが、その時に見つけたものを応用しました
billの単語の意味
ワーカーがターゲットとするデータ収集先のOpenAPI仕様は以下の通りです。
入力パラメータ
法律案審査・処理API入力引数
出力パラメータ
法律案審査・処理API入力引数
提供されるデータも多く、かなり詳細な情報をサービスできそうです。
Httpリクエストの準備物
まず、上記入力パラメータのHttpリクエスト要求のために必要なものが何かを見てみると
URL、Httpリクエストメソッド(ここでは出ていませんが、Getです。Postは不可ㅠ)、KEY、Type、pIndex、pSizeの値です。
KEY値の場合、他のOpenAPIと同様に別途認証キーを取得する必要があります。
開発の話をしている場所なので、参考資料があればリンクを貼って進めようと思っていたのですが…まだマイナーなせいか、認証キーの取得方法を説明している記事がGoogleに全く出てきませんね…とりあえずここでは簡単に図を中心に説明すると…
ホームページ
ホーム画面でマイページボタンを押します。
マイページ
認証キー発行画面
上の画面で、活用用途や内容については、私の場合は公的データコンテスト用目的と簡単に書きました。内容を記入する部分については、特に心配する必要はなく、1~2行の不適切な理由でなければすぐに承認されると思います。
このようにして取得したキーが、上記のHTTPの説明に必要なKEYを発行してくれます。
上記のURLに一度リクエストを送信してみます(次回のアップデートでは、必ずメソッドも入れてほしいという願いを込めて)
go```go package main import ( "encoding/json" "fmt" "io" "log" "net/http" "net/url" ) func procErr(e error, msg string) { if e != nil { log.Fatal(e, msg) } } func main() { rawUrl := "https://open.assembly.go.kr/portal/openapi/TVBPMBILL11" parsedUrl, err := url.Parse(rawUrl) procErr(err, "err after parsedUrl") params := url.Values{} KEY := "your_key" //change this procErr(err, "err after req, 27") req.Header.Add("Content-Type", "application/json") req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36") params.Add("KEY", KEY) params.Add("Type", "json") // JSON 좋.아 params.Add("pIndex", "1") // 이건 왜 integer로 표시했는지 모르겠지만 string params.Add("pSize", "1") client := &http.Client{} reqURL := fmt.Sprintf("%s?%s", rawUrl, params.Encode()) req, err := http.NewRequest(http.MethodGet, reqURL, nil) res, err := client.Do(req) procErr(err, "err, after client do") defer res.Body.Close() var jsonMap any response, err := io.ReadAll(res.Body) procErr(err, "err after read response") err = json.Unmarshal(response, &jsonMap) procErr(err, "err after json unmarshal") fmt.Println(jsonMap)
```
上記のようなコードを使って、まず動作を確認することができました。
ヒントを一つお伝えすると、Headerを必ず追加する必要があります。このAPIを円滑に使用したい場合は。(半ば脅迫)
その後、Get URLに必須のパラメータを埋めます。
pIndexとpSizeの値はintegerを使うように書かれていますが、string型を適用しても動作します。(理解不能)
Typeにはxml方式も使用できますが、私はJsonのやり取りを選びました。
結果値
map[TVBPMBILL11:[map[head:[map[list_total_count:103305] map[RESULT:map[CODE:INFO-000 MESSAGE:正常処理されました。]]]] map[row:[map[AGE:22 BILL_ID:PRC_T2U4C0A8B2Z0A1Y3Y5G2H0F0G5E4F4 BILL_NAME:政治資金法一部改正法案 BILL_NO:2203179 CMT_PRESENT_DT: CMT_PROC_DT: CMT_PROC_RESULT_CD: COMMITTEE_DT: COMMITTEE_PROC_DT: CURR_COMMITTEE: CURR_COMMITTEE_ID: LAW_PRESENT_DT: LAW_PROC_DT: LAW_PROC_RESULT_CD: LAW_SUBMIT_DT: LINK_URL:https://likms.assembly.go.kr/bill/billDetail.do?billId=PRC_T2U4C0A8B2Z0A1Y3Y5G2H0F0G5E4F4 PROC_DT: PROC_RESULT_CD: PROPOSER:高東進議員など10人 PROPOSER_KIND:議員 PROPOSE_DT:2024-08-23 RST_MONA_CD:HS39431V RST_PROPOSER:高東進] map[AGE:22 BILL_ID:PRC_O2M4M0L8L2T0R1S3Q5R1P4Q0Y2W2X6 BILL_NAME:政党法一部改正法案 BILL_NO:2203178 CMT_PRESENT_DT: CMT_PROC_DT: CMT_PROC_RESULT_CD: COMMITTEE_DT: COMMITTEE_PROC_DT: CURR_COMMITTEE: CURR_COMMITTEE_ID: LAW_PRESENT_DT: LAW_PROC_DT: LAW_PROC_RESULT_CD: LAW_SUBMIT_DT: LINK_URL:https://likms.assembly.go.kr/bill/billDetail.do?...
上記のような結果を得ることができます。
私は別途、そのオブジェクトを直接制御するために、すべてのstructを定義して値を取得していますが、これもまた数え切れないほどのデバッグを重ねましたね。
その値をstructにUnmarshalする作業は、文字数制限のため、恐らく次回の記事で続けることになると思います。
1つのリクエストが、開発記の1編を丸々使い果たしてしまいましたね。もちろん、このOpenAPIの使用特性上、APIの利用とstruct構造を整えるのに多くの時間を費やしましたが。この部分を、他の皆さんが同じ苦労をしないような方法を見つけなければなりませんね。
上記のリクエストの中で、想定通りに動作しなかったり、私の変なコードに不満が生じた方は、タバコも差し入れください。