見出し画像

FlaskでPythonのプログラムをWebアプリにする話

 この記事はPythonの他に、HTML、cssを使ったwebサイトを作る知識があることを前提に話が進んでいきます。ご注意ください。


はじめに

作ったプログラムを誰でも使えるようにしたい

 Pythonのプログラムを始めて3か月、思うことがあるのですよ。
誰でも使える作品を作りたい!って。
 Pythonってソースコードそのままだと開発環境がないといけないじゃないですか。実行ファイルに変換するとWindowsセキュリティに引っかかるし、そもそもパソコンがないと使えません。そんなことでどんな端末でも使えるブラウザで動くようにしよう!ってなりました。

ブラウザでプログラムを動かす利点

 今時の人は皆スマホを持っているので、Pythonのプログラムをブラウザで動くようにすれば、URLの共有だけでファイルのダウンロードや実行なしに作品をいつどこでも触ってもらうことができます。
 ついでにHTMLを使えるので、GUIの作成がとても簡単です。CSSを一緒に使えばTkinterよりも簡単に華やかで美しいGUIができるかもしれません。

javascriptとの違い

 javascriptでプログラムを実行すると、クライアント側での処理になるので制限があります。例えばフォームで送信された画像の処理などです。その点Pythonはサーバーサイドでの処理などで、重要な処理もすることができます。
 適材適所ではありますが、Pythonをwebブラウザに組み込むと、javascriptではできなかった処理もこなせるかもしれませんね。
 私はブラウザ上のアニメーションなどの見た目に関わる処理をjavascript、受け取ったデータを加工して送信するのをPython、という感じで理解してます。

webアプリケーションにしてみよう

 前回の記事で画像から文字を抽出するプログラムをつくりました。今回はそのプログラムを流用・改造して、ブラウザ上から画像をアップロードしたらその画像の文字を抽出して表示させるプログラムにしましょう。

必要なツールをインストール

 webフレームワーク(webアプリ制作のツール)をインストールします。今回はFlaskを使うので、通常のモジュールのようにインストールしてください。今回の必須ツールはこれだけです。

pip install Flask

今回の環境

今回の開発環境です。
Python 3.11
    Flask 3.0.1

    pillow 10.2.0
    pytesseract 0.3.10
Tesseract-OCR 5.3.3
 太字以外は、私のプログラムの動作に必要なだけです。自身の作るものに合わせてツールは選択してください。

挙動の確認

 とりあえずで以下のコードを実行しました。
2行目appにFlaskを定義、4行目からはapp内の動作、8行目で実行です。

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "hello world"

if __name__ == "__main__":
    app.run()

実行するとターミナルには以下の文字列が表示されます。

 * Serving Flask app 'serverSys'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit

 WARNINGはデバックモードであることを示すアラートなので無視でいいです。サーバーを立ち上げてURLを表示してくれるのでアクセスすると、
"hello world"とブラウザに書かれているはずです。

HTMLファイルを表示させよう

 同じ階層に"templates"というフォルダをつくり、適当なhtmlファイルを入れてください。私はこのようなファイルを入れました。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>Flaskで表示するhtml</title>
        <link rel="stylesheet" href="/static/style.css">
    </head>

    <body>
        <h1>このhtmlは、Flaskによって表示してます</h1>
        <p>
            テストテストテスト
        </p>
    </body>
</html>

また"static"というフォルダを作り、htmlで指定をすればcssやjavascriptも適用できます。

*{
    box-sizing: border-box;
    background-color: aqua;
}
h1{
    padding-top: 30px;
    text-align: center;
}
p{
    text-align: center;
    color: #999;
}

pythonファイルもhtmlファイルを表示するために以下のようなコードにしました。

from flask import Flask, render_template
app = Flask(__name__)

@app.route("/")
def htmlView():
    html = render_template("index.html")
    return html

if __name__ == "__main__":
    app.run()

実行してwebブラウザを表示させると、以下のような表示になりました。

表示されたhtml

背景色や文字の中央寄せなど、cssの適用されたhtmlが表示されましたね。

htmlからPythonへファイルを送る

 では今回の本題の一つ、ファイルの読み込みをしていきましょう。
まずhtml内でのformの作成です。以下のコードを差し込みます。

<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="image" accept=".jpg,.jpeg,.png" required>
    <input type="submit" value="送信">
</form>

 actionで"upload"というリクエストを送信します。enctypeはデータ送信時のエンコードタイプです。requiredと記述することでfileが選択されていないときにリクエストを送信しないようにしました。
 他は通常のhtmlでも使う属性ですね。送信ボタンを押したらinputでfileが選択されていたら、uploadというファイルを送信します。

次にPythonのほうで記述していきます。

@app.route("/upload", methods = ["post"])
def upload():
    uploadImage = request.files["image"]
    img = Image.open(uploadImage)
    img.save("画像パス")

    return redirect(url_for("htmlView"))

@app.route("/upload", methods = ["post"])
 "upload"というリクエストを受信したら起動するソースです。postメソッドで受け取ってるのでその記述もします。
uploadImage = request.files["image"]
 ここでリクエストで送信されたファイルを変数に格納しています。
あとはpillowの画像保存の記述です。
 これを実行してファイルを送信すれば、指定したパスにファイルが保存されるはずです。

webアプリとして仕上げよう

 これらと前回の記事を基に、今回作るプログラムを完成させていきます。
前回のソースコードをモジュールにして組み込み、リクエストによってページがリダイレクトされたときに画像から読み取ったメッセージを表示するようにしましょう。

前回のプログラムをモジュール化するので、少し修正します。

import pytesseract
from PIL import Image

class charRec:

    def __init__(self, img):
        self.opImg = Image.open(img).convert("L")

    def Convert(self):
        width,height = self.opImg.size
        for x in range(width):
            for y in range(height):
                b = self.opImg.getpixel((x,y))
                if b >= 125 :
                    self.opImg.putpixel((x,y),(255))

        return pytesseract.image_to_string(self.opImg, lang="jpn")

画像の取得を、ディレクトリではなく変数から入手するようにしました。

続いてサーバー処理のコードに、画像から読み取った文字列をセッションデータとして送ります。

from flask import Flask, render_template, request, redirect, url_for, session
import gazosyori


app = Flask(__name__)
app.secret_key = "alLiUm_Luu-331124120050316"

# ---------サーバー処理------------------ #

@app.route("/")
def htmlView():
    picData = session.get("picData", "")
    html = render_template("index.html", picData=picData)
    return html

@app.route("/upload", methods = ["post"])
def upload():
    receiveImg = gazosyori.charRec(request.files["image"])
    result = receiveImg.Convert()

    session["picData"] = result
    return redirect(url_for("htmlView"))

session[変数]=変数、と記述することでsession中はhtmlなどに変数の値を送ることができます。
これをFlaskに内蔵されたテンプレートエンジンのJinja2を使います。htmlファイルに以下の記述をします。

{% if picData %}
    <p>{{ picData }}</p>
{% endif %}

先ほどセッション中に送信したpicDataという変数が空ではない場合に、picDataを表示するというコードです。

前回も使ったこの画像

いつもの.png

をブラウザから読み取らせてみましょう。ファイルを選択して送信すると、以下のような表示になります。

実行結果

 pタグでくくったので、"テストテストテスト"と同じ表示で、画像の読み込みから値の送信までできていることが確認できました。
 これで今回の目標である"ブラウザ上から画像をアップロードしたらその画像の文字を抽出して表示させるプログラム"は完成です。

最後に

今回のコードです。
まずはFlaskを使うサーバー動作に関するコード

from flask import Flask, render_template, request, redirect, url_for, session
import gazosyori


app = Flask(__name__)
app.secret_key = "任意の文字列"

# ---------サーバー処理------------------ #

@app.route("/")
def htmlView():
    picData = session.get("picData", "")
    html = render_template("index.html", picData=picData)
    return html

@app.route("/upload", methods = ["post"])
def upload():
    receiveImg = gazosyori.charRec(request.files["image"])
    result = receiveImg.Convert()

    session["picData"] = result
    return redirect(url_for("htmlView"))

if __name__ == "__main__":
    app.run()

次にtesseractで文字認識をするモジュール(詳しくは前回の記事を参照)

import pytesseract
from PIL import Image

class charRec:

    def __init__(self, img):
        self.opImg = Image.open(img).convert("L")

    def Convert(self):
        width,height = self.opImg.size
        for x in range(width):
            for y in range(height):
                b = self.opImg.getpixel((x,y))
                if b >= 125 :
                    self.opImg.putpixel((x,y),(255))

        return pytesseract.image_to_string(self.opImg, lang="jpn")

最後にブラウザに表示させるhtml

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>Flaskで表示するhtml</title>
        <link rel="stylesheet" href="/static/style.css">
    </head>

    <body>
        <h1>このhtmlは、Flaskによって表示してます</h1>
        <p>
            テストテストテスト
        </p>

        <form action="/upload" method="post" enctype="multipart/form-data">
            <input type="file" name="image" accept=".jpg,.jpeg,.png" required>
            <input type="submit" value="送信">
        </form>

        {% if picData %}
            <p>{{ picData }}</p>
        {% endif %}
    </body>
</html>

cssは動作に関係しないので自由に記述して大丈夫です。

 webフレームワークのFlaskを使い、動的動作をするwebアプリケーションを作成しました。
 サーバーサイドでの処理があるだけでwebサイトでできることが増えますね。次回はデータベースを使った処理を記述するので、よろしければまたご覧ください。


この記事が気に入ったらサポートをしてみませんか?