QiitaAPI v2の仕様を深堀し、使えるツールを作るための技術的要素を洗い出そう！ ～ただのパクリツールからの脱却へ

## はじめに: この記事を書いた目的について

この記事は、今日公開したQiita APIを利用したツールをよりよいものにするための次のActionである、QiitaAPI v2の仕様調査結果をまとめたものになります。
個人的にPDCAが下手なのでその練習も兼ねて。

### Plan && Do:ツールを公開。振り返ってみると…

[こちら](https://qiita.com/developer-kikikaikai/items/b0a70362386698a1ea2d)なんですが、見返すと人が作ったアイディアをなんとなく別の手段で作っただけのパクリツールだな～。
情報限定的 && 重いので、ちゃんとアップデートしようと思います。

### Check:何が悪かったのか？

前回記事でPlan, Doまで行ったので、これに対してのcheckしていきます。

- Planの問題: 自分の頭で考えず、ただ人と同じ機能を実現しただけのツール仕様にしてしまった。
    - 何も考えていないのは自分の身にならないし、いいものも作れない。
        - まずその検討の為に必要な、各APIで何が出来るかの技術的調査をちゃんとしていない。
- Doの問題: ツールとしての中途半端感が強い
    - 上記技術調査の下地がないから、今出来たことをただ並べただけのようになってしまっていた。
    - 重い。Viewsとストック数の為にアクセス過多になってしまった。
        - どちらも使用しているAPIの仕様をちゃんと把握していないから、過不足が多い

### Action:次はどうすればいいか？

- まずは公開機能を利用してものづくりをするための第一歩の**技術的要素を洗い出す**。
- 上記要素を踏まえて使いやすいツールを検討し、ツールをアップデートする。

(当たり前のことをやっていなかっただけですね。。。)

### Plan: この記事の目的⇒ツールを作るための仕様調査をちゃんとまとめよう！

というわけで、ツールを作るためにQiitaAPI v2仕様で良さそうなポイントを抜粋し調査・整理をしていきたいと思います。本記事を踏まえ、元のツールもアップデートしていきます。
とにかく仕様の全容を網羅しよう！という記事ではないので、もっと他のAPIも知りたい！という方はこちらの既存記事も参照ください。
[Qiita API v2 の概要(非公式)](https://qiita.com/tag1216/items/b0b90e30c7e581aa2b00)

## Qiita API v2の特徴をピックアップ

各API深堀の前に、まずはQiita API v2の特徴・制限について簡単に触れていきます。

### アクセストークンによる認証制御

不要にユーザーの秘密にしている情報(非公開投稿記事等)が取得できないよう、**アクセストークン**という仕組みにより認証制限がかけられています。

- アクセストークンはユーザーログイン時にしか作成出来ない。

二段階認証によるログイン
⇒ブラウザログインしてページ経由でアクセストークンの作成
というルートでしか作成出来ないようにして、安易ななりすましを防止出来る仕組みになっています。

- アクセストークンは、作成時に表示される1度だけしか外部に公開されない

アクセストークンは、例えトークンを作成したユーザーであっても**2度とユーザー設定のページから表示されることがない**ため、情報流出の可能性が減ります。
(その分手間がありますけどね)

### アクセス回数制限

以下のようにアクセス制限が行われています。

- アクセストークンを使った状態⇒ユーザごとに1000回/1時間
- それ以外⇒IPアドレスごとに60回/1時間

### [ページネーション](https://qiita.com/api/v2/docs#%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3)によるデータ分割

Qiita v2では、APIのレスポンスで送信される投稿情報に対して以下のような情報をくっつけています。

|要素|概要|範囲|
|:---|:---|:---|
|page|開始ページ番号|1～100|
|per_page|1ページ辺りの投稿数|1～100|

pageの仕様としては、レスポンスヘッダに`Link`タグが付加され、次のページは`<url>; rel="next"`にurlが設定されるので、順次nextがなくなるまでGETを繰り返すということを期待しているようです。
(動かしてみると、クエリで指定したページ数とよりもnextのページが多い気がするのが疑問)

Linkヘッダのパース処理はこのような感じに実装してみました。

```python:Linkヘッダーのパース
    #headerがHTTPレスポンスヘッダー。nextが無かったら""を返却
    def _get_next_link(self, header):
        #Linkタグがないならreturn
        if not self.HTTP_PROP_LINK in header:
            return ""
        #{'Link': '<url>; rel="first",<url>; rel="prev" , <url>; rel="next", <url>; rel="last"'}
        linklist=re.split(r',', header[self.HTTP_PROP_LINK])
        #<url>; rel="xxx"で分割
        for link_raw in linklist:
            #無駄文字をsubで削除後、splitで;で分割。[url, rel=XXX]の形になる
            link_split=re.split(r';', re.sub(r"[<>\" ]", "", link_raw))
            # rel="next"を採用
            if link_split[1] == 'rel=next':
                return link_split[0]
        #nextが無かった
        return ""
```

## API共通情報

ここでは共通情報をまとめていきます。

### アクセス先

qiita.com。以下のように使います。

```
https://qiita.com/api/v2/items
```

### [アクセストークン認証方法](https://qiita.com/api/v2/docs#%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3)

HTTPリクエストヘッダーに以下を設定します。認証が必須なAPIはこちらの指定が必要となり、本情報が正しくないと`401 Unauthorized`となります。

```
Authorization: Bearer 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd
```

### [投稿情報](https://qiita.com/api/v2/docs#%E6%8A%95%E7%A8%BF)

投稿情報はJson形式のオブジェクト({})で表現されます。
ここでは使えそうなものを抜粋。ほぼすべての情報がこのオブジェクトに詰まっています。

|キー|概要|備考|
|:---|:---|:---|
|**id**|記事の固有ID|他のAPIでも利用されます|
|url|記事のURL|Webページ作成で使えそう|
|**title**|記事のタイトル||
|**rendered_body**|HTML形式の記事本文|Webページで扱うのに便利|
|**body**|Markdown形式の記事本文|バックアップに便利|
|created_at|作成日|date-time形式(2018-10-07T16:40:35+09:00みたいな)|
|updated_at|更新日|date-time形式|
|tags|タグ情報||
|**likes_count**|いいね数|実は記事一覧取得APIでは取得できなかったりする。|
|**page_views_count**|views数|認証なしでは取得できない。<br>また、**一覧取得時では認証ありでもなぜか取得できない**。|
|comments_count|コメント数||
|user|記事作成者のユーザー情報|ユーザー名や投稿数等を持つ。人気ユーザーの調査に使うのは面白いかも|
|private|限定共有状態かどうかを表すフラグ|認証なしでは取得できない(要確認)|

## 記事一覧取得API

記事の一覧を取得するAPI群の説明です。リクエスト、レスポンス形式は共通です。

### リクエスト情報

以下リクエストクエリが使用できます。

|query|概要|範囲|
|:---|:---|:---|
|page|ページ番号|1～100。デフォルトは1|
|per_page|1ページ辺りの投稿数|1～100。デフォルトは20|

### レスポンス

JSON形式の[投稿情報]()リスト。

### リクエストURL

#### [GET /api/v2/authenticated_user/items](https://qiita.com/api/v2/docs#get-apiv2authenticated_useritems)

- 認証中のユーザの投稿の一覧を作成日時の降順で返します。
- 認証：必須

#### [GET /api/v2/users/:user_id/items](https://qiita.com/api/v2/docs#get-apiv2usersuser_iditems)

- 指定されたユーザの投稿一覧を、作成日時の降順で返します。
- 認証：必須でない(ただし認証なしの場合は取得情報に制限あり)

#### [GET /api/v2/items](https://qiita.com/api/v2/docs#get-apiv2items)

- 指定されたユーザの投稿一覧を、作成日時の降順で返します。
- 認証：必須でない(ただし認証なしの場合は取得情報に制限あり)

## 記事詳細取得API

記事一覧取得APIで取得した記事のid(記事urlの最後のハッシュ値と同じ)を利用して、記事の情報を取得します。
こちらのフォーマットは一意でないので、各URLに対してリクエスト/レスポンスに対する説明記載していきます。(ないものは省略)

### リクエストURL

### [GET /api/v2/items/:item_id](https://qiita.com/api/v2/docs#get-apiv2itemsitem_id)

- 投稿を取得します。
- 認証：必須でない(ただし認証なしの場合は取得情報に制限あり)

#### レスポンス

JSON形式の[投稿情報]。

### [GET /api/v2/items/:item_id/stockers](https://qiita.com/api/v2/docs#get-apiv2itemsitem_idstockers)

- 投稿をストックしているユーザ一覧を、ストックした日時の降順で返します。
- 認証：必須でない

### リクエスト情報

以下リクエストクエリが使用できます。

|query|概要|範囲|
|:---|:---|:---|
|page|ページ番号|1～100。デフォルトは1。|
|per_page|1ページ辺りの投稿数|1～100。デフォルトは20|

#### レスポンス

JSON形式のユーザー情報のリスト(投稿情報に含まれるユーザー情報と同様)。
stock数を取得するならリスト数を数えればいいですね。

### [GET /api/v2/items/:item_id/comments](https://qiita.com/api/v2/docs#get-apiv2itemsitem_idcomments)

- 投稿に付けられたコメント一覧を投稿日時の降順で取得します。
- 認証：必須でない

### リクエスト情報

以下リクエストクエリが使用できます。

|query|概要|範囲|
|:---|:---|:---|
|page|ページ番号|1～100。デフォルトは1|
|per_page|1ページ辺りの投稿数|1～100。デフォルトは20|

#### レスポンス

JSON形式のコメント情報(オブジェクト)のリスト。要素を抜粋

|キー|概要|備考|
|:---|:---|:---|
|**rendered_body**|HTML形式のコメント本文|Webページで扱うのに便利。ユーザー情報とうまく絡めればslackみたいな表現も可能かもですね。|
|**body**|Markdown形式のコメント本文|バックアップにいいのかな|
|created_at|作成日|date-time形式(2018-10-07T16:40:35+09:00みたいな)|
|updated_at|更新日|date-time形式|
|user|ユーザー情報|ユーザー名や投稿数等を持つ。|

## 仕様を踏まえ、Qiita API v2をどうツールで使うといいのか？

### Qiitaと連携したアプリ向けなら一覧取得APIで十分

投稿情報にかなりの情報が詰まっているし、いいね数までは取得できるので記事一覧取得APIで大体事足りますね。
また、bodyのフォーマットがhtml向け、Markdown向けの2種類あるので、Webページ向けにも記事のエクスポートにも使える親切設計になっている
⇒Qiitaと連携したアプリケーションを作りたい！という方向であれば、こちらの情報をしっかり取得できるようになっていると便利ですね。

こうなってくるとtag情報もうまく使ってフィルタリングも出来るようにはしたいですね。

### 投稿者向けには記事詳細取得APIの情報も欲しい

一覧で取得した投稿情報だと閲覧数やストック数が取れないし、自分のツール作成の動機もストック数気になるだったので、この辺りの情報は投稿者的には見れるようにしたいですね。
また、投稿者向けという意味だと記事に連動したコメントも残せるので、うまく使えばコメントも含めて他のサイトへ移植なんてことも技術的には出来そう。

### アクセス数制限があるので最小限のアクセスを心がける

特に記事の量が増えてくるとViewsもストック数も取ろうとするとリクエスト数が凄い事になるので、必要なケースでは無駄なアクセスしなくてもいいように気を付けないといけないです。

### アクセストークンがなくても一覧は取れる

自分の記事へのアクセスにはアクセストークン必須なのかと思ったけど、情報抜けがあるだけなんですよね。
そうなると一覧が強いQiitaAPIとしては、アクセストークン作成の手間がなくても一覧が取れる仕組みが欲しくなりますね。

## Do: ツールのアップデートポイント

- 各種情報一覧が取得可能な汎用ツールにする。
- 設定ファイル経由でデータの表示/非表示が可能なようにする。
- versionに依存しない作りにしておく
- 汎用性確認がてら、本ツールを利用して前回のStock数等取れるツールを作成する。

⇒[作成しました](https://qiita.com/developer-kikikaikai/items/646e9f1ca8e1475e2dec)

## 参考

[Qiita API v2公式仕様](https://qiita.com/api/v2/docs#%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3)
[Qiita API v2 の概要(非公式)](https://qiita.com/tag1216/items/b0b90e30c7e581aa2b00)
[Qiita API v2 を使って自身の全投稿をエクスポートする Python スクリプトを書いた](https://qiita.com/sta/items/5074df5fcb81d890897b)