目次
- 目次
- はじめに
- ルーティング
- HTTP用ツール
- テンプレートエンジン
- ファイルの送受信
- Cookieの設定
- HTTPステータスコードのハンドリング
- HerokuでBottleを使う時にIPアドレスとポートを設定する
- webサーバを起動した時に、自動でwebブラウザを開く
- 他のWebフレームワークとのパフォーマンス比較
- bottleでwebsocketを使って非同期通信をする方法
- より詳しくBottleを学びたい人は
- 参考資料
- MyEnigma Supporters
はじめに
最近、趣味でWebアプリを作っているのですが、
bootleというPythonのWebサーバを使っています。
Pythonは昔から少し使ったことがあったので、
Pythonでサーバサイドのコードを書きたいなと思っていました。
PythonのWebサーバライブラリではDjangoやFlaskが有名ですが、
Web初心者には少し高機能すぎるようなので、
自分は軽量サーバライブラリであるBottleを選びました。
Bottleはライブラリと言いながらも、
一つのpythonファイルのみで構成されており、
あまり凝ったことをしない場合には非常にシンプルで使いやすいらしいです。
今回はこのBotttleに関する簡単な説明を書きたいと思います。
詳細は冒頭の『Python エンジニア養成読本』の第5章で説明されているため、
詳しく知りたい方は読んでみることをおすすめします。
ルーティング
bottleには、接続されたURLとpython関数をリンクさせる
ルーティング機能があります。
例えば、下記のようなコードを使うと
@route('/hoge') def hoge(): return template('index')
/hogeというURLに接続すると、
hoge関数が呼ばれて、
viewフォルダの中にあるindex.htmlが
ブラウザに返されます。
HTTP用ツール
BottleにはHTTPの用の様々なメソッドが準備されています。
HTTPそのものの説明に関してはこちらを参照下さい
各HTTPメソッドに応じて関数を振り分けたい場合は、
下記のようにデコレータを設定します。
GETメソッド
@route('/hoge')
or
@get('/hoga')
POSTメソッド
@route('/hoge', method='POST')
or
@post('/hoga')
PUTメソッド
@route('/hoge', method='PUT')
or
@put('/hoga')
DELETEメソッド
@route('/hoge', method='DELETE')
or
@delete('/hoga')
Request
Requestオブジェクトを使うと、
下記のようなクライアントから送信されるデータを利用することができます。
まず、
from bottle import request
した状態で、
HTTPヘッダ request.headers
クッキー request.cookies.get('key')
クエリパラメータ request.query
クエリパラメータは、HTTPのGETメソッドで取得できる
URLの後ろに付いてくるデータです。
「URLクエリパラメータ」とは何か、 どのような場合に「除外」するべきなのか?[第4回] | Googleアナリティクスとは/衣袋教授のGoogleアナリティクス入門講座 | Web担当者Forum
POSTパラメータ request.forms
添付ファイル request.files.get('name')
などからデータを取得することができます。
Response
逆に、サーバからクライアントにデータを送信する時は、
Respinseオブジェクトを使います。
まず、
from bottle import response
した状態で、
HTTPステータスコードの設定 response.status=404
HTTPヘッダの設定 response.set_header()
クッキーの設定 response.set_cookie("hoge","hoga")
とすることで、クライアント側にデータを送ることができます。
テンプレートエンジン
bottleには、テンプレートエンジンという機能があり、
htmlのひな形を読み込んで、
その中を引数で置き換えたページを表示することができます。
サーバアプリの中で、
return template('form', name='hogehoge')
とすると、./views/form.tplというテンプレートhtmlファイルを読み込んで
その中の{{name}}という部分をhogehogeと置き換えたhtmlが送信されます。
bottleのテンプレートエンジンで
個人的にすごいなと思ったことは、
HTMLファイル内でpythonコードを埋め込むことができることです。
例えば、下記のように行頭に%を置くと、
pythonコードを記述することができます。
% from time import gmtime, strftime % d=strftime("%Y", gmtime()) % wareki=int(d)-1988 <h1>今年は平成{{wareki}}年です。(西暦{{d}}年)</h1> % import datetime % today = datetime.date.today() % newyearsday = datetime.date(int(d), 1, 1) % dday=today-newyearsday % remainratio=round((365.0-float(dday.days))/365.0*100.0,2) % remaindays=str(remainratio) <h1>今日で今年の約{{remainratio}}%が過ぎました。</h1>
こちらをtemplate関数で返すようにすると、
下記のようにpythonの計算結果が、ブラウザに表示されます。
データ表示以外にも、
python条件分岐による
HTMLの表示の制御などもできます。
Web初心者には非常に嬉しいですね。
ファイルの送受信
Bottleを使ったファイルの送受信方法について説明します。
ファイルの受信
クライアント側からファイルを受信する場合は、
まずHTMLでPOSTメソッドのファイルアップロードの
インターフェースを作ります。
<form action="/upload" method="post" enctype="multipart/form-data"> <div class="form-group"> <label class="control-label" for="upload">Select a CSV file: <input type="file" name="upload"> </div> <div class="form-group"> <input type="submit" value="Upload" class="btn btn-primary"> </div> </form>
続いて、Webサーバ側では、
下記のようにrequestオブジェクトの
files.getメソッドを使って
ファイルオブジェクトを取得できます。
@route('/upload', method='POST') def do_upload(): upload = request.files.get('upload') name, ext = os.path.splitext(upload.filename) #file check if ext not in ('.csv'): return template("index",msg="The file extension is not allowed.") upload.save("/tmp",overwrite=True)
ファイルオブジェクトはfilenameという変数でファイル名を取得でき、
saveメソッドで指定してディレクトリにファイルを保存できます。
overwrite引数をtrueにするとファイルがある場合でも上書きされます。
ファイルの送信
ファイルの送信は
static_fileメソッドの返り値をreturnすることで、
クライアント側にファイル送信することができます。
return static_file("sample.txt", root='/tmp')
上記のサンプルプログラムの場合、
/tmpディレクトリのsample.txtというファイルを
クライアント側に送信しています。
Cookieの設定
Webサービスで、クライアントを認識したい時などに使われる
Cookieの設定は下記のAPIを使うことで設定するができます。
Cookieの設定
クライアントにCookieを設定する場合は、
responseオブジェクトのset_cookie関数を使います。
hogeというキーのクッキーを設定する場合は、
下記のようにします。
requst.set_cookie("hoge", 10) #hogeというkeyのデータに10を代入
クッキーのデータを暗号化したい場合は、secret引数にシークレットキーを入れます。
requst.set_cookie("hoge", 10, secret="secretkey")
また、クッキーの有効期限やドメイン指定などは、
パラメータを設定すればOKです。
詳しくはAPIドキュメントを参照下さい。
Cookieの取得
リクエストを受け取った
クライアントからのCookie情報を取得する場合は、
requestオブジェクトのget_cookie関数を使います。
hogeというキーのクッキーのデータを取得する場合は、
下記のようにします。
request.get_cookie("hoge")
クッキーが設定されていない場合に、
デフォルト値を返してもらいたい時は、
default引数に設定すると、そのように動いてくれます。
request.get_cookie("hoge",default=0) #hogeが設定されていない時は0が返ってくる
default引数を設定しなかった場合はNoneが返ります。
また、前述のクッキーの設定の際にsecretを設定した場合、
secret引数に同じシークレットキーを設定しなkれば
Cookieの削除
Cookieの削除は、responseオブジェクトから、
delete_cookie関数を使います。
クライアントのhogeというクッキーを削除したい場合は、
下記のようにします。
response.delete_cookie("hoge");
HTTPステータスコードのハンドリング
下記の記事で説明されている通り、
HTTPにはステータスコードというものがあります。
bottleでは上記のステータスコードが発生した時に、
それを拾ってエラーハンドリングをすることも簡単です。
例えば、500番 Internal Server Errorの
ステータスコードが来た時に、
トップURLにリダイレクトしたい時は、
下記のようにerrorというデコレータで、
ステータスコードを指定し、
その後に実行したい関数を書くだけです。
@error(500) def error_500(error): """Internal Server Errorの場合""" # トップにリダイレクト redirect("/")
HerokuでBottleを使う時にIPアドレスとポートを設定する
bottleではrun関数でサーバを起動しますが、
run関数ではIPアドレスとポートを指定する必要があります。
ローカル環境と、Herokuのデプロイ環境では、
IPとポート、そしてデバックモードなどは変わるので、
自分は下記のように、ローカル環境とHerokuのデプロイ環境を認識した上で、
run関数を実行するようにします。
まず、下記のherokuのコマンドで環境変数を設定します。
heroku config:set HEROKU=true
そして、下記のコードで先ほどの環境変数の有無で
run関数の引数を変えるようにします。
if __name__ == '__main__': if os.getenv("HEROKU")==None: run(host="localhost", port=(os.environ.get("PORT",5200)), debug=True, reloader=True) else: run(host="0.0.0.0", port=(os.environ.get("PORT",5200)))
このようにすることで、
ローカル開発環境とHerokuデプロイ環境を
同じソースコードで実行できます。
webサーバを起動した時に、自動でwebブラウザを開く
デバック時は、webサーバが立ち上がった時に
自動でブラウザでアクセスしてくれると便利です。
そんな時は、pythonのデフォルトモジュールである
webbrowserを使うと便利です。
下記のようにすると、
webサーバを立ち上げた時に、自動でブラウザを開いてくれます。
import webbrowser as web web.open('http://localhost:5200') run(host="localhost", port=5200, debug=True, reloader=True)
他のWebフレームワークとのパフォーマンス比較
下記の記事でBottleのWebサーバとしての
パフォーマンスが評価されています。
上記の比較結果によると、
テンプレートエンジンなどの最低限の
Webフレームワークとしての機能を持っていながらも
他のフレームワークと比べて非常に高速であることがわかります。
bottleでwebsocketを使って非同期通信をする方法
上記の方法を使って、
イベント的な通信は出来ますが、
HTTPは基本的にリクエスト、レスポンス型の通信システムなので
定期的な通信をするのは難しいです。
一般的なWeb技術で非同期通信をする場合には、
HTML5から導入された、
websocketという技術を使うことが多いようですが、
bottleのデフォルトに機能にはこのwebsocketは含まれていません。
そんな時は、下記のリンクの
bottole-webscketを使うと簡単に
bottleのWebサーバで、websocketを使うことができます。
まずサーバ側は下記のようにします。
from bottle import get, run, template from bottle.ext.websocket import GeventWebSocketServer from bottle.ext.websocket import websocket import time @get('/') def index(): return template('index') seq=1 @get('/websocket', apply=[websocket]) def echo(ws): global seq while True: ws.send("Get"+str(seq)) seq+=1 time.sleep(1) run(host='192.168.3.5', port=8001, server=GeventWebSocketServer)
そして、テンプレートは下記のようにします。
<!doctype html> <head> <meta charset="utf-8" /> <title>WebSocket Echo Test</title> <style> li { list-style: none; } </style> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script> <script> $(document).ready(function() { if (!window.WebSocket) { if (window.MozWebSocket) { window.WebSocket = window.MozWebSocket; } else { $('#messages').append("<li>Your browser doesn't support WebSockets.</li>"); } } ws = new WebSocket('ws://192.168.3.5:8001/websocket'); ws.onopen = function(evt) { $('#messages').append('<li>WebSocket connection opened.</li>'); } ws.onmessage = function(evt) { $('#messages').append('<li>' + evt.data + '</li>'); } ws.onclose = function(evt) { $('#messages').append('<li>WebSocket connection closed.</li>'); } $('#send').submit(function() { ws.send($('input:first').val()); $('input:first').val('').focus(); return false; }); }); </script> </head> <body> <h2>Bottle Websockets!</h2> <form id="send" action='.'> <input type="text" value="message" /> <input type="submit" value="Send" /> </form> <div id="messages"></div> </body> </html>
上記のサーバを立ち上げた状態で、
PCのWebサーバに接続すると、
下記のように一秒毎に一つづつ増える
メッセージが表示されます。
ちなみに、
iPhoneでアクセスしても問題なく表示されました。
Webサーバの情報を気軽にスマホなどで見たい場合には、
便利ですね。
より詳しくBottleを学びたい人は
下記の資料がおすすめです。
Bottle: Python Web Framework — Bottle 0.13-dev documentation
また、どうしても解決できない問題などがある場合は
下記のようなQ&Aサイトで質問してみると、
かなりの確率で回答がもらえると思います。
自分も上記のサンプルコードを作る上で、
何回か質問させてもらいましたが、
その日の内に返信をもらうことができました。
参考資料
Pythonの軽量WebフレームワークBottleを試してみた(その1) - ルーティング編 (Advent Calendar 23日目) | アライドアーキテクツ エンジニアブログ
【Python+Bottle】Twitterの位置付きツイートを可視化するWebサービスをリリースしてみた - Qiita
【Python】bottle, Beautifulsoup, geopyを使って野球の地図を作ってみました - Lean Baseball
「bottle」+「jinja2」を使ってサイト構築 2 jQueryとBootstrap導入 - from umentu import stupid
サイトをFreeBSD10+python3.3+Bottleで作り直すメモ(9)-Cookie — KtJ Dragon
MyEnigma Supporters
もしこの記事が参考になり、
ブログをサポートしたいと思われた方は、
こちらからよろしくお願いします。