Qiita API を利用して記事を投稿する


# はじめに

[Qiita API][api-docs] を利用してローカルの markdown ファイルを記事としてアップロードする python スクリプトを作成しました。作成したスクリプトのテストも兼ねて、作業内容をまとめてみます。

1. 投稿内容の作成
1. 投稿用のヘッダ情報の作成
1. 投稿処理
1. 投稿後のレスポンス情報から ID 情報を取得
1. 投稿内容が書かれたファイルのヘッダ部分に ID を追加

[api-docs]: https://qiita.com/api/v2/docs

# 背景

以前から自分で書いたメモなどの単純なドキュメントファイルに関しては、一括して [GitLab.com][gitlab] のプライベートリポジトリで管理していました。今回、 Qiita に投稿してみようと思ったのですが、 Qiita に投稿する内容は、都度書いて Qiita で管理するということも考えました。が、やはりローカル環境でテキストを書きたいということもあり、 Qiita API を活用して、ローカル環境から投稿するようにしました。まだまだ粗削りですが、とりあえず投稿して、記事を修正した場合は、修正版をコミットすることまではできました。

```sh
$ python3 --version
Python 3.6.6
```

[gitlab]: https://gitlab.com/

# 投稿内容の作成

ローカルのファイル群は [Hugo][hugo] を活用しています。そのため、投稿する内容以外にヘッダ情報が付与されています。ここを活用して、 Qiita に投稿するヘッダ情報も管理するようにします。
今回は、下記のようなヘッダ情報と投稿内容を作成していると仮定します。

(`hoge.md`)

```md
+++
title = "テスト投稿"
draft = true
tags = ["test"]

[qiita]
coediting = false
gist = false
tweet = false
id = ""
+++

hogehoge
```

[hugo]: https://gohugo.io/

# 投稿用のヘッダ情報の作成

上記で作成した `hoge.md` からヘッダ部分を読み込み、投稿用のヘッダ情報を作成します。

```py
import re
import toml

def load_file(filepath):
    # ファイル内容の読み込み
    with open(filepath) as f:
        buf = f.read()

    # ヘッダ部分と投稿する内容を分離します。
    # ここでは、 '+++' がヘッダ部分の範囲を指定すると決め打ちしています。
    # また、 hugo の関係上ヘッダが toml で記載されているので、
    # toml パーサで内容を読み込んでいます。
    header = re.match(
        r'^\+\+\+$.+?^\+\+\+$',
        buf,
        flags=(re.MULTILINE | re.DOTALL))
    body = buf[header.end() + 1:]
    header = buf[header.start() + 4:header.end() - 4]
    header = toml.loads(header)

    # ヘッダ情報から Qiita へ投稿するヘッダ情報へ修正します
    item = {
        'title': header['title'],
        'private': header['draft'],
        'tags': [{'name': tag} for tag in header['tags']],
        'coediting': header['qiita']['coediting'],
        'gist': header['qiita']['coediting'],
        'tweet': header['qiita']['tweet'],
        'id': header['qiita']['id'] if 'id' in header['qiita'] else '',
    }

    # 投稿内容を格納
    item['body'] = body

    return item
```

# 新規投稿の作成

Qiita API を利用して、 `load_file` 関数で取得する内容を投稿します。投稿には、 requests を利用して post 処理を行います。

```py
import requests

def post(item):
    url = 'https://qiita.com/api/v2/items'
    token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    headers = {'Authorization': 'Bearer {}'.format(token)}

    res = requests.post(url, headers=headers, json=item)

    return res
```

Qiita API を利用して新規の投稿を作成する場合は、アクセストークンが必要となります。 `token` の部分に自分で取得したアクセストークンを設定してください。

# 投稿したレスポンスから ID を取得しヘッダを更新

投稿に成功するとレスポンスに ID 情報が付与されます。この ID を自分のローカル環境にあるファイルのヘッダ情報に入れておけば、 Qiita の記事との対応関係が取れることになります。そこで、今回はレスポンスに入っている情報をもとに、ヘッダ部分を書き換えるようにしています。

```py
import toml

def add_qiita_id(filepath, item, res):
    item['qiita']['id'] = res.json()['id']
    data = '+++\n{}+++\n{}'.format(toml.dumps(header), body)

    with open(filepath, mode='w') as f:
        f.write(item)
```

Qiita から返ってくる ID を記録しておくと、後から記事を修正した場合に、ローカルの内容を同じ記事に反映することが可能となります。

# 処理フロー

```py
if __name__ == "__main__":
    filepath = 'hoge.md'
    item = load_file(filepath)
    res = post(item)
    add_qiita_id(filepath, item, res)
```

# 最後に

これでローカル環境で書いた内容をそのまま投稿することができるようになりました。ただ、まだまだ課題はありますし、エラー処理やらアクセストークンの管理やら対応していない部分が多いです。ざっと考えられる課題を挙げておきます。

* アクセストークンの管理がテキストファイル想定
* タグがついていないとエラーする
* hoge.md に必要なヘッダ情報が抜けた場合のエラー処理
* toml を利用してしまったので標準環境以外が入ってしまった
* ローカル環境は改行を無視するが、 Qiita では改行が改行として扱われるので、表示形式が異なってしまう
* エラー処理が抜けている
* 自動投稿への対応