Qiita APIをGo言語で叩いて人気記事を取得する

Blog Single

QiitaAPIをGo言語で叩く

今回は、Qiita APIを利用して人気のある記事を取得するプログラムを作成します。

Go言語を使ってAPIを叩くクライアントプログラムを書いていきますが、様々な方法でAPIを叩けるので、Go言語には興味ないけどQiita APIは使いたいという方は、ぜひ自分の好きな言語で作ってみたり、curlコマンドやブラウザなどから叩いたりしてみてください。

QiitaのAPIドキュメントはこちらから

Qiita API v2ドキュメント – Qiita:Developer

様々なAPIがありますが、今回は投稿されている記事から過去一週間の日付毎の人気のある投稿を取得するようなプログラムを作成します。

投稿に関するAPIはこちらから

投稿 GET /api/v2/items

実行環境

% go version
go version go1.10.1 darwin/amd64

Qiitaアクセストークンの取得

アクセストークンがなしで認証しない状態だとIPアドレスごとに1時間に60回までのリクエスト制限があるので、Qiitaのアクセストークンを取得します。

取得方法はQiitaにログインして、

設定>アプリケーション>個人用アクセストークン

まで移動すると、新しくトークンを発行すると書かれたリンクがあるので、そこに遷移すると以下のようなページが表示されます。

アクセストークンの説明を適当に入れます。

スコープの項目で発行したアクセストークンに与える権限を設定できますが、今回は書き込みや登録を行うAPIを利用しないので、read_qiitaだけにチェックして「発行する」ボタンを押します。

そうすると鍵マークのアイコンの横に文字列が表示されるので、それがアクセストークンです。

アクセストークンが確認できるのは作成した直後だけなのでコピーしてどこかに保管します。

アクセストークンを忘れると、新しくアクセストークンを発行する必要があります。(再発行自体は簡単に行えます。)

プログラム

今回のプログラムはgoの標準パッケージのみで行うので、パッケージのインストールは不要です。

過去一週間分のストック30以上の記事を取得するプログラムです。

main.go

package main

import (
    "net/http"
    "io/ioutil"
    "encoding/json"
    "net/url"
    "os"
    "fmt"
    "time"
)

// jsonのパース用に構造体を定義
type Data struct {
    Url string `json:"url"`
    Title string `json:"title"`
    LikesCount int `json:"likes_count"`
    ReactionsCount int `json:"reactions_count"`
    PageViewsCount int `json:"page_views_count"`
}

// Qiitaからデータを取得
func fetchQiitaData(accessToken string, targetDate time.Time) []Data {

    baseUrl := "https://qiita.com/api/v2/"
    action := "items"

    // 1リクエストあたり最大30件取得
    baseParam := "?page=1&per_page=30"

    // monthだけintではなくMonth型のため型変換が必要
    year, month, day := targetDate.Date()
    targetDay := dateNum2String(year, int(month), day)

    year, month, day = targetDate.AddDate(0,0, 1).Date()
    nextDay := dateNum2String(year, int(month), day)

    // 投稿の検索クエリを作成
    // 検索クエリ stocks:>NUM created:<YYYY-MM-DD created:>YYYY-MM-DD
    // 指定日に投稿されたストック数30以上の記事を取得
    varParam := "&query=stocks:>30+created:>="+targetDay+"+created:<"+nextDay

    endpointURL, err := url.Parse(baseUrl+action+baseParam+varParam)
    if err != nil {
        panic(err)
    }

    b, err := json.Marshal(Data{})
    if err != nil {
        panic(err)
    }

    var resp = &http.Response{}
    // qiitaのアクセストークンがない場合はAuthorizationを付与しない
    if len(accessToken) > 0 {
        resp, err = http.DefaultClient.Do(&http.Request{
            URL:    endpointURL,
            Method: "GET",
            Header: http.Header{
                "Content-Type":  {"application/json"},
                "Authorization": {"Bearer " + accessToken},
            },
        })
    } else {
        resp, err = http.DefaultClient.Do(&http.Request{
            URL:    endpointURL,
            Method: "GET",
            Header: http.Header{
                "Content-Type":  {"application/json"},
            },
        })
    }
    defer resp.Body.Close()

    if err != nil {
        panic(err)
    }

    b, err = ioutil.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }

    var datas []Data

    if err := json.Unmarshal(b, &datas); err != nil {
        fmt.Println("JSON Unmarshal error:", err)
        return nil
    }
    return datas
}

// データの出力
func outputQiitaData (datas []Data) {

    for _, val := range datas {
        fmt.Println(val.LikesCount,val.Title, val.Url)
    }

    fmt.Println()
}

// 年月日の数値を文字列に変換
func dateNum2String(year int, month int, day int) string {
    return fmt.Sprintf("%d-%d-%d",year,month,day)
}

func main() {

    // アクセストークン取得
    qiitaToken := os.Getenv("QIITA_TOKEN")

    var baseDate time.Time

    fmt.Println("いいね数       タイトル        URL")

    // コマンドライン引数がある場合 YYYY-MM-DD の形で指定 不適切な方の場合エラー
    var err error
    if len(os.Args) >= 2 {
        arg := os.Args[1]
        layout := "2006-01-02"
        baseDate, err = time.Parse(layout, arg)
        if err != nil {
            panic(err)
        }
    } else {
        // 引数がない場合
        baseDate = time.Now()
    }
    // 一週間前から取得
    startDate := baseDate.AddDate(-1, 0, -6)
    numOfDates := 7

    // 対象の日付から一週間分のデータを取得
    for i:=0;i<numOfDates;i++{
        targetDate := startDate.AddDate(0,0, i)
        fmt.Printf("%d-%d-%d\n",targetDate.Year(), int(targetDate.Month()), targetDate.Day())

        datas := fetchQiitaData(qiitaToken, targetDate)

        outputQiitaData(datas)
    }
}

実行方法

コマンドライン引数でYYYY-MM-DDの形式で指定すると指定した日から1週間前までの記事を取得できます。指定しなければ現在の日付から一週間前までの記事を取得します。

先ほど取得したアクセストークンは、環境変数に追加するか、実行する際に指定するとアクセストークンをセットします。

環境変数についてはこちらから

「パスを通す」とは何か

環境変数にセットできているか確認

%  echo $QIITA_TOKEN
<<YOUR ACCESS TOKEN>>

<<YOUR ACCESS TOKEN>>に先ほど発行したアクセストークンが表示されていれば環境変数にセットできています。

実行

環境変数に追加している場合

% go run main.go

環境変数に追加していない場合
<<YOUR ACCESS TOKEN>>の部分にアクセストークンを貼り付けます

% QIITA_TOKEN=<<YOUR ACCESS TOKEN>> go run main.go

実行結果

いいね数        タイトル        URL
2017-8-10
44 LINQ好きはLINQを書く時にどう考えているのか? https://qiita.com/koba-a-koba/items/57639feadb54139f6cb9
38 デバッグしやすい、解析しやすいコードを書こう https://qiita.com/tamura__246/items/018d654f9c1509625753
46 本をめくるようなWEBページがつくれる「Turn.js」のススメ https://qiita.com/yuxio/items/f3c1f0e9d3de2b089e24

2017-8-11
178 一から始める機械学習(機械学習概要) https://qiita.com/taki_tflare/items/42a40119d3d8e622edd2
56 結構マジメにVimのテスト環境を整えてみる(Python用) https://qiita.com/lighttiger2505/items/96d4cda9074f9719bc82

# 省略

2017-8-16
66 IntelliJ 公式ショートカット一覧 PDF 日本語版 https://qiita.com/cypher256/items/8ce0527a46bd7cfe43eb
51 HEARTalk で自然な応答システムを作る https://qiita.com/ksasao/items/e7ca345b3402d41b29d2

このように出力されました。

次に取得したい日付を指定

% go run main.go 

実行結果

% go run main.go 2018-06-01
いいね数        タイトル        URL
2017-5-26
45 Visual Studio Codeの機能拡張「Vim」の為のメモ https://qiita.com/kuroyakov/items/e58d0a2ac96df166a088
107 TypeScriptのInterfaceとType aliasの比較 https://qiita.com/tkrkt/items/d01b96363e58a7df830e
48 モダンブラウザで試すcss3(曇りガラス風の背景と文字の透過) https://qiita.com/kfunnytokyo/items/bae21b69a43bfd24b31c

# 省略

2017-6-1
71 [Kotlin] Kotlin初学者が学習時に参考にした情報等 https://qiita.com/natsuyoshi_jr/items/2e141ffb493ca5f20c7e
49 Unicode と UTF-8 (とその仲間たち) のざっくりした違い https://qiita.com/QUANON/items/09396ae9c3aab7129ac2

これで簡単に面白い記事を探すことができます。

まとめ

QiitaのAPIが気になって今回このようなプログラムを書きましたが、もっと使いやすくするならコマンドラインツールとして細かいコマンドライン引数などを指定できるようにしたり、ただデータが欲しいならばcsvファイルなど出力したりとできそうです。

公開されているWeb APIを触るというのは結構楽しいのでぜひ気になった方は色々触ってみてください。

Posted by MatsumotoKazuki
PHPやJavaで開発を行っているエンジニア。 LOLというゲームの試合を観戦するのが好きです。

Other Posts: