[Python]Qiita APIとテンプレートエンジンJinja2で記事を投稿する

# はじめに
本記事では、[Qiita API](https://qiita.com/api/v2/docs)での記事投稿の際に記事本文にPythonのテンプレートエンジンである[Jinja2](https://qiita.com/tags/jinja2)の出力を利用する方法を紹介します。
本記事自身をQiita APIとJinja2テンプレートを使って投稿しています。

# テンプレートの準備
`template.md.j2`というファイル名のJinja2に使うテンプレートを用意します。
本記事では、記事一覧表示のサンプル部分の表を変数で埋め込みます。

# 記事一覧表示のサンプル
自分の投稿記事一覧を取得して、表形式で表示します。

### 実行結果

| No | 記事 |
|:-----------|:------------|
| 1 | [Qiitaの類似記事を見つける（TF-IDF+コサイン類似度）](https://qiita.com/mochi_yu2/items/9002e114119085f86199) |
| 2 | [iPadからVSCodeとJupyter Notebookを使う](https://qiita.com/mochi_yu2/items/860ea0abbc9101a90afa) |
| 3 | [VPSのDocker上のアプリにアクセス制限をかける](https://qiita.com/mochi_yu2/items/239c1b4261bafece42f3) |
| 4 | [【Java】Apache HttpClientの使い方メモ](https://qiita.com/mochi_yu2/items/c88dba5cc7d2f1f81c0b) |
| 5 | [Qiita APIでの投稿記事取得のページネーション実装2種(Python)](https://qiita.com/mochi_yu2/items/556b91e9f25b2eec83de) |
| 6 | [QiitaのSelenium全記事からワードクラウドを作る](https://qiita.com/mochi_yu2/items/c8980e888ab82b949abf) |
| 7 | [Ubuntu 20.04のdocker build時にタイムゾーン選択を求められた時の対処メモ](https://qiita.com/mochi_yu2/items/af80a3adc7ff70e58807) |
| 8 | [Selenium(Python)の個人的スニペット集](https://qiita.com/mochi_yu2/items/e2480ae3b2a6db9d7a98) |
| 9 | [[Chrome] SeleniumでBasic認証を突破する](https://qiita.com/mochi_yu2/items/ce598ec57afe44453e98) |
| 10 | [SeleniumでWeb監視をしてLINEに通知する](https://qiita.com/mochi_yu2/items/c1b5fb8279166598cb92) |
| 11 | [SeleniumとHeadless ChromeでページをPDFに保存する](https://qiita.com/mochi_yu2/items/a845e52b8aa677f132bf) |
| 12 | [【Selenium】Googleスプレッドシートを読み込んで会員登録フォームに入力する](https://qiita.com/mochi_yu2/items/00aed4443f6895f022f9) |
| 13 | [【Chrome】Seleniumでページ全体のスクリーンショットを撮る](https://qiita.com/mochi_yu2/items/d1a2c91776eba93c25e5) |
| 14 | [Seleniumでクリック前に要素にスクロールさせる](https://qiita.com/mochi_yu2/items/bfca3be05dfc1cd41853) |
| 15 | [SelenideでSeleniumのテスト用サイトをテストしてみる](https://qiita.com/mochi_yu2/items/b145aea848e0336825ab) |
| 16 | [Selenide + Allureでクリック毎にスクリーンショットを取得して、テストレポートに添付する](https://qiita.com/mochi_yu2/items/1a2676dac0438063defd) |
| 17 | [Selenium + Headless Chrome + Jupyter Notebook + Docker](https://qiita.com/mochi_yu2/items/84cf2a4eb6cb70667c91) |


### テンプレート部分

```jinja:テンプレート

| No | 記事 |
|:-----------|:------------|
{% for article in sample_articles -%}
| {{ loop.index }} | [{{ article.title }}]({{ article.url }}) |
{% endfor %}

```

### 記事一覧の取得
Qiita APIを使って記事を取得するPythonコードは以下を使っています。

```python:記事一覧の取得
# Qiita APIを利用して、記事を取得する
def get_articles(query = None):
    ret = []
    # クエリパラメータの準備
    params = {
        'page': '1',
        'per_page': '100'
    }
    if (query is not None):
        params['query'] = query
    req_headers = {}

    url = "https://qiita.com/api/v2/items?" + urllib.parse.urlencode(params)
    # Qiita APIから結果を取得
    req = urllib.request.Request(url, headers=req_headers)
    with urllib.request.urlopen(req) as res:
        body = json.load(res)
        for article in body:
            ret.append({"url": article["url"], "title": article["title"]})

    return ret
```

# テンプレートエンジンでの本文作成
Jinja2を使ったMarkdown形式の投稿本文作成部分のPythonコードは以下です。

```python:本文作成
# Jinja2を使って、投稿記事の本文を生成する
def create_article_content(sample_articles):
    env = Environment(loader=FileSystemLoader('.'))
    template = env.get_template('template.md.j2')
 
    data = {'sample_articles': sample_articles}
    markdown_text = template.render(data)
    return markdown_text
```

# 記事投稿
Qiita APIを使って記事を投稿するPythonコードは以下を使っています。

```python:記事の投稿
# Qiita APIを利用して、記事を投稿する
# 以下では、「"private": True」として、限定共有状態で投稿します
def post_article(token, content):
    url = 'https://qiita.com/api/v2/items'
    data = {
        "body": content,
        "private": True,
        "tags": [
            {
                "name": "Qiita"
            },
            {
                "name": "QiitaAPI"
            },
            {
                "name": "Python"
            },
            {
                "name": "jinja2"
            }
        ],
        "title": "[Python]Qiita APIとテンプレートエンジンJinja2で記事を投稿する",
    }
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + token
    }

    req = urllib.request.Request(url, json.dumps(data).encode(), headers)
    try:
        with urllib.request.urlopen(req) as res:
            body = json.load(res)
            print("投稿記事URL: " + body["url"])
    except urllib.error.HTTPError as err:
            print(err.code)
    except urllib.error.URLError as err:
            print(err.reason)
```

# メイン関数
ここまでを合わせたPythonのメイン関数部分は、以下のようになります。

```python:メイン関数
def main():
    # 自分の投稿記事を取得
    sample_articles = get_articles('user:mochi_yu2')
    # 投稿記事のMarkdown形式の本文をJinja2で生成
    content = create_article_content(sample_articles)
    # Qiitaに投稿
    post_article(ACCESS_TOKEN, content)

if __name__ == "__main__":
    main()
```

# 参考
本記事の作成にあたり以下の記事を参考にさせていただきました。

https://qiita.com/RyoMa_0923/items/528303906a55f9b568e0

https://qiita.com/hoto17296/items/8fcf55cc6cd823a18217

https://www.python.ambitious-engineer.com/archives/760

# 付録: Pythonコード全体
```python:post_article.py
import urllib.request
import json
from jinja2 import Template, Environment, FileSystemLoader

# Qiita APIのアクセストークン
ACCESS_TOKEN = 'YOUR ACCESS_TOKEN'

# Qiita APIを利用して、記事を取得する
def get_articles(query = None):
    ret = []
    # クエリパラメータの準備
    params = {
        'page': '1',
        'per_page': '100'
    }
    if (query is not None):
        params['query'] = query
    req_headers = {}

    url = "https://qiita.com/api/v2/items?" + urllib.parse.urlencode(params)
    # Qiita APIから結果を取得
    req = urllib.request.Request(url, headers=req_headers)
    with urllib.request.urlopen(req) as res:
        body = json.load(res)
        for article in body:
            ret.append({"url": article["url"], "title": article["title"]})

    return ret

# Qiita APIを利用して、記事を投稿する
# 以下では、「"private": True」として、限定共有状態で投稿します
def post_article(token, content):
    url = 'https://qiita.com/api/v2/items'
    data = {
        "body": content,
        "private": True,
        "tags": [
            {
                "name": "Qiita"
            },
            {
                "name": "QiitaAPI"
            },
            {
                "name": "Python"
            },
            {
                "name": "jinja2"
            }
        ],
        "title": "[Python]Qiita APIとテンプレートエンジンJinja2で記事を投稿する",
    }
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + token
    }

    req = urllib.request.Request(url, json.dumps(data).encode(), headers)
    try:
        with urllib.request.urlopen(req) as res:
            body = json.load(res)
            print("投稿記事URL: " + body["url"])
    except urllib.error.HTTPError as err:
            print(err.code)
    except urllib.error.URLError as err:
            print(err.reason)

# Jinja2を使って、投稿記事の本文を生成する
def create_article_content(sample_articles):
    env = Environment(loader=FileSystemLoader('.'))
    template = env.get_template('template.md.j2')
 
    data = {'sample_articles': sample_articles}
    markdown_text = template.render(data)
    return markdown_text

def main():
    # 自分の投稿記事を取得
    sample_articles = get_articles('user:mochi_yu2')
    # 投稿記事のMarkdown形式の本文をJinja2で生成
    content = create_article_content(sample_articles)
    # Qiitaに投稿
    post_article(ACCESS_TOKEN, content)

if __name__ == "__main__":
    main()
```