Responderについて
2018年の10月ぐらいに公開されたPythonのWebフレームワーク
当時からスター稼ぎまくりのおばけプロジェクトだった
たまたま仕事で触る機会があったので自分のメモがてらまとめておく
Python界隈では有名な方が作成しており
FlaskとFalcon(これはやったことない)のいいとこ取りな上に
独自の設計思想が加えられて強靭無敵最強のフレームワークに出来上がっている
Responderの売りは恐ろしいぐらいのシンプルさ
Responderをimportするだけでもう使える
import responder
Elixirでのtrotでも同じようなことを言っていますが
今の流行りはシンプルさと制作がいかに早く行えるかということ
その点Responderは両方を兼ね備えている。うーん、良い
学習コストもかなり低く、1時間ぐらいドキュメントを見ただけで何と無く書けてしまう
responderのインストールとサーバーの立ち上げ
公式ではpipenvを使うことを推奨している
無くてもinstall & 使用は可能だけど、素直に従う
pipenvのinstallも簡単で以下を叩くのみ
> pip install pipenv
そしてpipenvを使ってresponderをinstallする
あと、ついでに後にjanomeを使うのでついでにinstall
> pipenv install responder --pre
#これはしなくてもok
> pipenv install janome
では早速、responderでAPIを作成する
その前にプロジェクト用のディレクトリを用意しておく
mkdir cool_api cd cool_api
作成したファイルに以下を追加
./cool_api/server.py
import responder api = responder.API() if __name__ == '__main__': api.run()
pipenvを使って作成したserver.pyを実行する
> pipenv run python server.py INFO: Started server process [62605] INFO: Waiting for application startup. INFO: Uvicorn running on http://127.0.0.1:5042 (Press CTRL+C to quit)
やったぜ。上手くserverが立ち上がった
デフォルトでは5042番ポートを使用してサーバーが立ち上がるが
api.run(port=8000)
とすることでポートを変更することが可能
ただ、この状態では一切のendpointが無いので無能of the無能なため
簡単なendpointを実装する
シンプルな実装例(GETとPOST)
まずはシンプルなテキストを返すGET(/)を実装する
./cool_api/server.py
@api.route("/") #endpointを設定 def first_api(req, resp): #引数は基本的に固定 resp.text = "first success!!" #respに値を設定することで戻り値を作成
早速呼び出す
> curl http://localhost:5042/ first success!!%
上手く返ってきた
続いてjsonを返してみる
./cool_api/server.py
@api.route("/info") def human_info(req, resp): #jsonを返したいときは.mediaを指定する resp.media = {"user": "okb"}
> curl http://localhost:5042/info {"user": "okb"}%
いい感じっすわ
ページのレンダリングとbodyの値の取得
せっかくなので触れておく
まずはページのレンダリングから
server.pyの初回実行時に./cool_api の直下に
/static と /templates が作成されていると思う
/templates 直下に適当にindex.htmlを作成する
responderはデフォルトでjinja2をラップしているのでimport無しで使える
jinja2についての説明はカット
./cool_api/templates/index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Simple home</title> </head> <body> <h1>Welcome to my home</h1> <ul> <li>my name is okb</li> <li>I like elixir</li> <li>nice to meet you</li> </ul> {% block content %}{% endblock %} </body> </html>
/main をcallした時にこのファイルを返すようにする
./cool_api/server.py
@api.route("/main") def simple_homepage(req, resp): #.htmlファイルを返すときはresp.htmlを設定 & ファイル名を指定 resp.html = api.template("index.html")
とりあえずcurlで呼び出す
./cool_api/templates/index.html
> curl http://localhost:5042/main <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Simple home</title> </head> <body> <h1>Welcome to my home</h1> <ul> <li>my name is okb</li> <li>I like elixir</li> <li>nice to meet you</li> </ul> </body> </html>%
ブラウザで見れば上手くレンダリングされてるはず
最後にPOSTで送られてきたbodyの値を受け取る
やり方さえ分かってしまえば簡単であとは今まで通り
Javascriptを触る方にはお馴染みだと思うasyncとawaitを使う
pythonでも使えるやってresponder使ってて知った
./cool_api/server.py
@api.route("/easy-post") #asyncを指定する async def easy_post(req, resp): data = await req.media() #ここで値を受け取る input_data = data.get("input", "None data..?") resp.text = f"fetch: {input_data}"
受け取った値をテキストで返す
では呼び出す
> url -H "Content-Type: application/json" -X POST -d '{"input": "apple"}' http://localhost:5042/easy-post fetch: apple%
そこそこ便利なAPIを実装する
以前紹介&作成したjanomeを使用して日本語の形態素解析をAPIで行う
クラスメゾットに関しては使い回しですまぬ(若干アップグレードしてる
./cool_api/utils.py
import os from janome.tokenizer import Tokenizer import re regex = r"(https?|ftp)(:\/\/[-_\.!~*\'()a-zA-Z0-9;\/?:\@&=\+\$,%#]+)" pattern = re.compile(regex) class JanomeSpliter(): """return keywords(str) in array""" def __init__(self): self.t = Tokenizer() def parser(self, value_str, tag=u"名詞", validate=True): tokens = self.t.tokenize(str(value_str)) if validate: res = [self.validate_str(token.surface) for token in tokens if token.part_of_speech.split(',')[0] == tag] res = list(filter(lambda x: x != None, res)) return res else: res = [self.validate_str(token.surface) for token in tokens] res = filter(lambda x: x != None, res) return " ".join(res) def is_tag(self, value_str, tag): tokens = self.t.tokenize(str(value_str)) res = [self.validate_str(token.surface) for token in tokens if token.part_of_speech.split(',')[0] == tag] return True if res else False def validate_str(self, value_str): if re.match('[あ-んア-ン一-鿐]+', value_str): return value_str
server.pyの上部に以下を追記
./cool_api/server.py
from utils import JanomeSpliter j = JanomeSpliter()
APIを実装する。このAPIではレンダリングとjsonの両方をサポートするようにする
URLの値を取得するには際にはendpointで実行する関数に引数を追加する
./cool_api/server.py
@api.route("/ja-parser/{mode}") async def japanese_spliter(req, resp, *, mode): data = await req.media() user_input = data.get("input", False) if user_input: #受け取った値を形態素解析 splited_input = j.parser(user_input, validate=False) if mode == "temp": #form.htmlに形態素解析結果を渡す resp.html = api.template("form.html", split_result=splited_input.split(" ")) else: #jsonを返す resp.media = {"data": splited_input}
> curl -H "Content-Type: application/json" -X POST -d '{"input": "負けない思いを君に伝えたいよ"}' http://localhost:5042/ja-parser/api {"data": "\u8ca0\u3051 \u306a\u3044 \u601d\u3044 \u3092 \u541b \u306b \u4f1d\u3048 \u305f\u3044 \u3088"}%
あ、何か文字化けしてますね
postman使って呼び出すときちんと形態素解析されている
postmanでの実行結果
{ "data": "負け ない 思い を 君 に 伝え たい よ" }
最後にレンダリング用に新規にform.htmlファイルを作成する
jinja2の構文についてはやっぱりカット
./cool_api/templates/form.html
{% extends "index.html" %} {% block content %} <h2>parser form</h2> <!-- 送信先のendpointを指定(tempモード) --!> <form method="post" action="/ja-parser/temp"> {{ form }} <!-- 受け取る際には"input"をkeyとして取得できる --!> <textarea name="input"></textarea> <button type="submit">execute</button> </form> {% if split_result %} <p>parse result</p> {% for token in split_result %} <p>{{ token }}</p> {% endfor %} {% endif %} {% endblock %}
先ほどの/main でレンダリングするファイルをform.htmlに変更してブラウザから呼び出してみる
css当ててないのでデザインについてはお許しを...ッ!!
./cool_api/server.py
@api.route("/main") def simple_homepage(req, resp): resp.html = api.template("form.html")
で、formに適当にテキストを入力してexecuteを押す
よし、上手く返ってきた。いいね
長々と書きましたがresponderの便利さが伝われば光栄です