GitHubリポジトリのスター数でOSSランキングを作成する

GitHubリポジトリのスター数順にOSSをソートしランキング化する方法を紹介します。

## 成果物

##### 検索スクリプト

以下にスクリプトを公開していますのでご活用ください。

- [loftkun/github-api-samples](https://github.com/loftkun/github-api-samples)
  - [v3/search/repositories](https://github.com/loftkun/github-api-samples/blob/master/v3/search/repositories/search.sh)

後述する`page`パラメタをインクリメントしながら検索しますので便利です。

##### ランキング

本記事で紹介している方法で作成したGo言語のWebフレームワークランキングです。

- [Go言語Webフレームワークランキング](https://qiita.com/loftkun/items/1a9951d1864bebdc51e1)

## ランキング作成方法
### リポジトリをスター数順に検索＆ソートする

[GitHub REST API v3](https://developer.github.com/v3) の [Search API](https://developer.github.com/v3/search/)を利用します。

##### APIエンドポイント

```
https://api.github.com/search/repositories
```

##### APIパラメタ
詳細は[search/#parameters](https://developer.github.com/v3/search/#parameters)をご参照下さい。

パラメタ | 設定値 | 設定例
--- | --- | ---
q | 以下を含むクエリ<br>`検索キーワード(要URIエンコード)`<br>`in`<br>`language`<br>`stars` | web+framework
in | 検索対象<br>`name` : リポジトリ名<br>`description` : 概要<br>`readme` : README.md   | name,description,readme
language | 言語 | go
stars | スター数の条件 | >=1000
sort | ソートする項目 | start
order | ソート順序 | desc
per_page | 1ページあたりのitem数 | 10
page | ページ番号(1始まり) | 1

`per_page`と`page`については[Pagination](https://developer.github.com/v3/#pagination)をご参照下さい。
`page`パラメタを適宜インクリメントしながら複数回検索する必要があります。

### 検索コマンド

スター数1000以上のGo言語のリポジトリを対象に`web framework`をキーワードに検索するコマンドの例です。

```
LANGUAGE=go
KEYWORD=web+framework
STAR=1000
USER=your_username
PASS=your_password
page=1
curl -u ${USER}:${PASS} "https://api.github.com/search/repositories?q=${KEYWORD}+in:name,description,readme+language:${LANGUAGE}+stars:>=${STAR}&sort=stars&order=desc&per_page=10&page=${page}"
```

認証は必須ではありませんが制限(rate limit)にひっかかりにくいので実施してます。
上記例はBASIC認証ですがOAuth2認証にしたい方は以下記事を参照下さい。
[GitHub API を 叩く](https://qiita.com/loftkun/items/94d4ead8b1dfbc669ffb)

### 検索結果

`total_count`は全リポジトリ数です。
`items`はリポジトリの配列で要素数は最大で`per_page`です。
`stargazers_count`がスター数で降順にソートされています。


```json
{
  "total_count": 61,
  "incomplete_results": false,
  "items": [
    {
      "id": 21540759,
      "node_id": "MDEwOlJlcG9zaXRvcnkyMTU0MDc1OQ==",
      "name": "awesome-go",
      "full_name": "avelino/awesome-go",
      "private": false,
      "owner": {
        "login": "avelino",
        "id": 31996,
        "node_id": "MDQ6VXNlcjMxOTk2",
        "avatar_url": "https://avatars2.githubusercontent.com/u/31996?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/avelino",
        "html_url": "https://github.com/avelino",
        "followers_url": "https://api.github.com/users/avelino/followers",
        "following_url": "https://api.github.com/users/avelino/following{/other_user}",
        "gists_url": "https://api.github.com/users/avelino/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/avelino/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/avelino/subscriptions",
        "organizations_url": "https://api.github.com/users/avelino/orgs",
        "repos_url": "https://api.github.com/users/avelino/repos",
        "events_url": "https://api.github.com/users/avelino/events{/privacy}",
        "received_events_url": "https://api.github.com/users/avelino/received_events",
        "type": "User",
        "site_admin": false
      },
      "html_url": "https://github.com/avelino/awesome-go",
      "description": "A curated list of awesome Go frameworks, libraries and software",
      "fork": false,
      "url": "https://api.github.com/repos/avelino/awesome-go",
      "forks_url": "https://api.github.com/repos/avelino/awesome-go/forks",
      "keys_url": "https://api.github.com/repos/avelino/awesome-go/keys{/key_id}",
      "collaborators_url": "https://api.github.com/repos/avelino/awesome-go/collaborators{/collaborator}",
      "teams_url": "https://api.github.com/repos/avelino/awesome-go/teams",
      "hooks_url": "https://api.github.com/repos/avelino/awesome-go/hooks",
      "issue_events_url": "https://api.github.com/repos/avelino/awesome-go/issues/events{/number}",
      "events_url": "https://api.github.com/repos/avelino/awesome-go/events",
      "assignees_url": "https://api.github.com/repos/avelino/awesome-go/assignees{/user}",
      "branches_url": "https://api.github.com/repos/avelino/awesome-go/branches{/branch}",
      "tags_url": "https://api.github.com/repos/avelino/awesome-go/tags",
      "blobs_url": "https://api.github.com/repos/avelino/awesome-go/git/blobs{/sha}",
      "git_tags_url": "https://api.github.com/repos/avelino/awesome-go/git/tags{/sha}",
      "git_refs_url": "https://api.github.com/repos/avelino/awesome-go/git/refs{/sha}",
      "trees_url": "https://api.github.com/repos/avelino/awesome-go/git/trees{/sha}",
      "statuses_url": "https://api.github.com/repos/avelino/awesome-go/statuses/{sha}",
      "languages_url": "https://api.github.com/repos/avelino/awesome-go/languages",
      "stargazers_url": "https://api.github.com/repos/avelino/awesome-go/stargazers",
      "contributors_url": "https://api.github.com/repos/avelino/awesome-go/contributors",
      "subscribers_url": "https://api.github.com/repos/avelino/awesome-go/subscribers",
      "subscription_url": "https://api.github.com/repos/avelino/awesome-go/subscription",
      "commits_url": "https://api.github.com/repos/avelino/awesome-go/commits{/sha}",
      "git_commits_url": "https://api.github.com/repos/avelino/awesome-go/git/commits{/sha}",
      "comments_url": "https://api.github.com/repos/avelino/awesome-go/comments{/number}",
      "issue_comment_url": "https://api.github.com/repos/avelino/awesome-go/issues/comments{/number}",
      "contents_url": "https://api.github.com/repos/avelino/awesome-go/contents/{+path}",
      "compare_url": "https://api.github.com/repos/avelino/awesome-go/compare/{base}...{head}",
      "merges_url": "https://api.github.com/repos/avelino/awesome-go/merges",
      "archive_url": "https://api.github.com/repos/avelino/awesome-go/{archive_format}{/ref}",
      "downloads_url": "https://api.github.com/repos/avelino/awesome-go/downloads",
      "issues_url": "https://api.github.com/repos/avelino/awesome-go/issues{/number}",
      "pulls_url": "https://api.github.com/repos/avelino/awesome-go/pulls{/number}",
      "milestones_url": "https://api.github.com/repos/avelino/awesome-go/milestones{/number}",
      "notifications_url": "https://api.github.com/repos/avelino/awesome-go/notifications{?since,all,participating}",
      "labels_url": "https://api.github.com/repos/avelino/awesome-go/labels{/name}",
      "releases_url": "https://api.github.com/repos/avelino/awesome-go/releases{/id}",
      "deployments_url": "https://api.github.com/repos/avelino/awesome-go/deployments",
      "created_at": "2014-07-06T13:42:15Z",
      "updated_at": "2019-01-05T14:58:22Z",
      "pushed_at": "2019-01-05T09:41:48Z",
      "git_url": "git://github.com/avelino/awesome-go.git",
      "ssh_url": "git@github.com:avelino/awesome-go.git",
      "clone_url": "https://github.com/avelino/awesome-go.git",
      "svn_url": "https://github.com/avelino/awesome-go",
      "homepage": "https://awesome-go.com/",
      "size": 4851,
      "stargazers_count": 38695,
      "watchers_count": 38695,
      "language": "Go",
      "has_issues": true,
      "has_projects": false,
      "has_downloads": true,
      "has_wiki": false,
      "has_pages": true,
      "forks_count": 5035,
      "mirror_url": null,
      "archived": false,
      "open_issues_count": 31,
      "license": {
        "key": "mit",
        "name": "MIT License",
        "spdx_id": "MIT",
        "url": "https://api.github.com/licenses/mit",
        "node_id": "MDc6TGljZW5zZTEz"
      },
      "forks": 5035,
      "open_issues": 31,
      "watchers": 38695,
      "default_branch": "master",
      "permissions": {
        "admin": false,
        "push": false,
        "pull": true
      },
      "score": 94.155464
    },
        ( 略 ： )
    {
        ( 略 ： )
    }
  ]
}

```

## 検索結果を整形

ランキングとして表示するには項目が多すぎますので見やすいフォーマットに加工します。

##### CSV ( or Json ) にする

以下項目を抽出します。

```
リポジトリ名,スター数,リポジトリURL,概要
```

`jq`コマンドでCSV形式に加工する例です。
なお、`| @csv`を削除するとjson形式になります。

```
curl -u ${USER}:${PASS} "https://api.github.com/search/repositories?q=${KEYWORD}+in:name,description,readme+language:${LANGUAGE}+stars:>=${STAR}&sort=stars&order=desc&per_page=10&page=${page}" | jq -r ".items[] | [.full_name , .stargazers_count, .html_url, .description] | @csv"
```

##### 整形結果

１行１リポジトリとなりすっきりしました。

```
"avelino/awesome-go",38695,"https://github.com/avelino/awesome-go","A curated list of awesome Go frameworks, libraries and software"
"gin-gonic/gin",23408,"https://github.com/gin-gonic/gin","Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin."
"astaxie/beego",18465,"https://github.com/astaxie/beego","beego is an open-source, high-performance web framework for the Go programming language."
"kataras/iris",13285,"https://github.com/kataras/iris","The fastest backend community-driven web framework on (THIS) Earth. HTTP/2, MVC and more. Can your favourite web framework do that? 👉 http://bit.ly/iriscandothat1 or even http://bit.ly/iriscandothat2"
"openfaas/faas",12576,"https://github.com/openfaas/faas","OpenFaaS - Serverless Functions Made Simple"
"labstack/echo",12566,"https://github.com/labstack/echo","High performance, minimalist Go web framework"
"go-kit/kit",12312,"https://github.com/go-kit/kit","A standard library for microservices."
"revel/revel",10638,"https://github.com/revel/revel","A high productivity, full-stack web framework for the Go language."
"go-martini/martini",10411,"https://github.com/go-martini/martini","Classy web framework for Go"
"boltdb/bolt",9314,"https://github.com/boltdb/bolt","An embedded key/value database for Go."
```

##### ノイズの除去

最終的なランキングデータとするには、ノイズ(検索キーワードは含まれているが目的に沿わない検索結果)の除去が必要です。
ノイズかどうか判断するには`description`や`README.md`(特に序章の紹介文)を読むのが良いです。
やはり最終的には人力が必要になりますが、ここまでの作業でスター数が多い順に絞り込まれていますので、
上から順に精査しノイズを除去していくと後戻りなくランキングトップXX(トップ10など)が出揃います。

##### Markdownにする

[CSV To Markdown Table Generator](https://donatstudios.com/CsvToMarkdownTable)のようなサイトでCSVをMarkdownのテーブルに変換できます。
Qiita等に投稿するのに便利です。

## まとめ

GitHubリポジトリのスター数順にOSSをランキングする方法と成果物を紹介しました。
スター数以外にもコミュニティの活発度、機能性や性能など、OSSを選定するための基準はたくさんあります。
開発に適したOSSを選定するための指標の1つとして本記事のランキング作成方法が手助けになりましたら幸いです。