投稿者「HEXADRIVE001」のアーカイブ

Python標準ライブラリで簡易掲示板

こんにちは
ビッシーです

 

前回に引き続き Python 標準ライブラリの小ネタということで、
簡単な掲示板機能を作ってみたいと思います。

 

Pythonには優秀なサードパーティのwebフレームワークがたくさんありますが、
ローカル環境などでミニマムな開発用ツールとして何か作りたいときには、
標準ライブラリのみでサクッと作ってしまう事もできるのではと思い、
今回は標準ライブラリ縛りで挑んでみました!

 

では、
実際にコードを紹介しながら、解説していきたいと思います。
(作成にあたって Python 3.4 を使用しています)

 

① HTTPサーバーの立ち上げ

標準ライブラリ http.server には SimpledHTTPRequestHandler というクラスがあり、
HTTPリクエストのパースとレスポンスヘッダの生成を簡単に行うことが出来ます。

 

http.server – Python 3.4.3 ドキュメント

 

デフォルトの場合、URLにあるファイルを返す単純なHTTPサーバーですが、
このクラスの do_GET というメソッドをオーバーライドすることで、
アプリケーションを簡単に実装することが出来ます。

 

 

import http.server
import urllib.parse

class http_handler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        # リクエスト内容をパース
        request = urllib.parse.urlparse(self.path)
        params = dict(urllib.parse.parse_qsl(request.query))

        # レスポンスを生成
        body = self.body(request.path, params)
        self.send_response(200)
        self.send_header('Content-type', 'text/html; charset=utf-8')
        self.send_header('Content-length', len(body))
        self.end_headers()
        self.wfile.write(body)

    def body(self, request, params):
        # ヘッダー
        response = "<html><header><title>sample</title></header><body>"
        # 本文
        response += "<h1>Hello, World!</h1>"
        # フッター
        response += '</body></html>'

        return response.encode('utf-8')

 

 

GETリクエストのパラメータを解釈するために、
urllib.parse を利用しています。
(こちらもPythonの標準ライブラリです)

 

リクエストハンドラの準備が出来たので、
TCPサーバーを生成してHTTPサーバー化させます。

 

import socketserver

if __name__ == "__main__":
    httpd = socketserver.TCPServer(("", 8000), http_handler)
    httpd.serve_forever()

 

ここまでのpythonコードを実行すると、
ローカルでHTTPサーバーが起動します。

 

ウェブブラウザで localhost:8000 にアクセスすると、

 

 

20150715_image_001

 

 

無事、HTTPサーバーにアクセスできました!

 

② SQLiteを利用して投稿を保存

今回はリクエストされたメッセージを保存するために、
SQLite データベースを利用します。

 

sqlite3 – Python 3.4.3 ドキュメント

 

こちらもPython標準ライブラリでサポートされています。

 

import sqlite3
import time

class database():
    def __init__(self, path):
        self.path = path
        self.create()

    def create(self):
        conn = sqlite3.connect(self.path, isolation_level='EXCLUSIVE')
        try:
            conn.execute("create table if not exists entry (id integer primary key, message text, date real);")
            conn.commit()
        except Exception as e:
            print(e)
            conn.rollback()
        finally:
            conn.close()

    def post(self, message):
        conn = sqlite3.connect(self.path, isolation_level='DEFERRED')
        try:
            conn.execute("insert into entry(message, date) values(?, ?);", (message, time.time()))
            conn.commit()
        except Exception as e:
            print(e)
            conn.rollback()
        finally:
            conn.close()

    def get(self):
        result = []
        conn = sqlite3.connect(self.path)
        try:
            result = [x for x in conn.execute("select id, message, date from entry;")]
        finally:
            conn.close()

        return result

# DB
db = database("entry.db")

 

シンプルな掲示板用として、
書き込み時刻とメッセージのみのテーブルを作成しています。

 

メソッドが3種類ありますが、
それぞれ必要なDBリクエストに対応した処理を書いています。

 

createでは、データベース処理の開始時に、
 必要なテーブルを生成しています。

 

postでは、投稿内容をテーブルに挿入しています。
 書き込み時刻保存のために time を利用して現在時刻を記録しています。

 

getでは、表示用に投稿内容を全て取得しています。

 

これで最低限必要なデータベース処理の準備が出来たので、
HTTPリクエストハンドラを書き直して投稿と表示ができるようにします。

 

HTTPリクエストハンドラでは、
DBにアクセスして記事の投稿・取得を行い、
HTMLドキュメントを生成して返しています。

 

また、
スタイルシート無しだとレイアウトが味気なかったため、
PapierというCSSライブラリを使用させて頂きました。

 

Papier

 

class http_handler(http.server.SimpleHTTPRequestHandler):

    (省略)

    def body(self, request, params):
        response = '<html><header><title>sample</title><link rel="stylesheet" href="https://cdn.rawgit.com/alexanderGugel/papier/master/dist/papier-1.0.0.min.css"></header><body class="bg-subtle"><div style="width:50%;margin:0 auto">'
        if request == "/get":
            # 投稿用フォームを挿入
            response += '<section class="panel"><header>Post from:</header><main><form method="GET" action="/post"><input type="text" name="message" placeholder="message"><input type="submit" value="送信"></form></main></section>'
            # 投稿内容を表示
            for e in sorted(db.get(), key=lambda e: e[2], reverse=True):
                response += '<section class="panel"><header>{0}</header><main>{1}</main></section>'.format(time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(e[2])), e[1])
        
        elif request == "/post":
            if not "message" in params:
                response += "<h1>Invalid Post</h1>"
            else:
                db.post(params["message"])
                response += '<meta http-equiv="REFRESH" content="1;URL=/get"><h1>Post Successed.</h1>'

        else:
            response += "<h1>Invalid Request</h1>"

        # フッター
        response += '</div></body></html>'
        return response.encode('utf-8')

 

ここまでの修正を適用してHTTPサーバーを立ち上げなおし、
localhost:8000/get をウェブブラウザでアクセスすると、
無事、投稿できる掲示板ページができあがりました。

 

 

20150715_image_002

 

 

メッセージを入力すると、メッセージがDBに記録されて
投稿がどんどん増えていきます。

 

                

 

日々の開発に役立つちょっとしたツールを開発するために、
Pythonは便利な開発環境ですね

 

プログラマは技術の引き出しが多ければ多いほど、
様々な問題に対応できるようになれます。

 

これからも積極的に各種テクニックを磨いて、
日々のコーディングに役立てていきたいと思います

 

それでは