Python3でPycURLを用いてteratail APIのJSONからPythonタグつきの質問を取得

teratailというプログラミングQ&AサイトのAPIからJSON形式で質問取得を試みた際のメモです。Pythonで試みたのでPythonに関連する質問の取得を試みています。環境は Windows 7, Python 3.5.1 です。

必要なPythonモジュールを用意

PycURL

  • PythoncURLライブラリを使うモジュールを利用しています。
    今回はHTTP(S)リクエストする際に用います。
    PHPを使ったことがあれば、PHPのcurl関数のような感じで利用できるイメージです。
  • この際にインストールしたバージョンは pycurl (7.43.0) です。

Windows

python -m pip install pycurl

LinuxmacOS

pip install pycurl

コード

  • BEARER = ''でteratailに登録後取得できるアクセストークンを設定します。
  • 無くても動作しますがteratailの仕様で1時間あたり30回までのアクセスになるそうです。アクセストークンがあれば1時間あたり300回までアクセス可能になるそうです。
# -*- encoding: utf-8 -*-
import io, json, pycurl, sys, urllib.parse

if __name__ != '__main__':
    sys.exit()

# teratail登録後、 https://teratail.com/users/setting/tokens で取得
BEARER = ''

class Teratail:
    API_URL = 'https://teratail.com/api/v1/tags/{}/questions?limit={}&page={}'

    __curl = None
    __tag = ''
    __limit = 20
    __page = 1
    __bearer = None
    __httpCode = 200

    def __init__(self, tag='', limit=20, page=1):
        self.__tag = tag
        self.__limit = limit
        self.__page = page

        # インスタンス生成
        self.__curl = pycurl.Curl()

    def __execute(self):
        json = None
        buf = io.BytesIO()

        headers = {}
        if self.__bearer:
            headers['Authorization'] = 'Bearer {}'.format(self.__bearer)

        self.__curl.reset()
        try:
            # Headerをセット
            headers = ['{}: {}'.format(*v) for v in headers.items()]
            self.__curl.setopt(pycurl.HTTPHEADER, headers)
            # リクエスト先をセット
            self.__curl.setopt(pycurl.URL, self.API_URL.format(self.__tag, self.__limit, self.__page))
            # MethodをGETにセット(デフォルト)
            #self.__curl.setopt(pycurl.CUSTOMREQUEST, 'GET')
            # SSL証明書の有効性を確認しない
            self.__curl.setopt(pycurl.SSL_VERIFYPEER, False)
            # pycurlの結果を変数に格納
            self.__curl.setopt(pycurl.WRITEDATA, buf)
            # 出力
            self.__curl.perform()
            # 取得したjsonデータをUTF-8にデコード
            json = buf.getvalue().decode('UTF-8')
        except (pycurl.error, TypeError):
            pass

        self.__httpCode = self.__curl.getinfo(pycurl.HTTP_CODE)

        buf.close()
        return json

    def setBearer(self, bearer):
        self.__bearer = bearer

    def getHttpCode(self):
        return self.__httpCode

    # http://docs.teratailv1.apiary.io/#introduction
    def getJson(self, tag=None, limit=None, page=None):
        if tag is not None:
            self.__tag = tag
        if limit is not None:
            self.__limit = limit
        if page is not None:
            self.__page = page
        return self.__execute()

tt = Teratail(limit=5)
tt.setBearer(BEARER)

# http://docs.teratailv1.apiary.io/#reference/(tag)/2/0
for tag in ('Python', 'Python 2.7', 'Python 3.x'):
    print(tag)

    jsonData = tt.getJson(tag=urllib.parse.quote_plus(tag))
    if jsonData is None:
        continue

    data = json.loads(jsonData)
    if 'questions' not in data:
        if 'meta' in data and 'message' in data['meta']:
            print('HTTP Status Code: {}'.format(tt.getHttpCode()))
            print('Message: {message}'.format(**data['meta']))
        continue

    print('Data:')
    for row in data['questions']:
        if 'id' not in row:
            continue

        # 質問ページ
        print('https://teratail.com/questions/{id}'.format(**row))
        # 質問のタイトル
        print(row['title'] if 'title' in row else None)
        # 質問を投稿したユーザの名前
        if 'user' in row and 'display_name' in row['user']:
            print(row['user']['display_name'])

        print()

    print()