よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

【Windows10とAndroid】PythonとbottleでポンコツWebアプリ改(markdownをhtmlに変換機能付き)

今回もbottleを使ったポンコツWebアプリを作ります。前回を改良しました。
前回のはブラウザのフォームから入力したテキストを表示しファイルに保存するだけのアプリでした。
今回の改良点はmarkdown形式で入力したテキストをhtml形式に変換し、元のテキストとhtml変換後のテキストをそれぞれ別々のファイルに保存します。



実行画像


実行するとブラウザが起動しページを表示

f:id:chayarokurokuro:20191107004055j:plain


入力ページ

f:id:chayarokurokuro:20191107004129j:plain


送信すると入力内容表示(元の文とhtml変換後のもの)

f:id:chayarokurokuro:20191107004032j:plain


ターミナルで確認中の元のテキストのファイル
(「ははは」の下に追加された)

f:id:chayarokurokuro:20191107004156j:plain


ターミナルで確認中のhtml変換後のファイル
(「ははは」の下に追加された)

f:id:chayarokurokuro:20191107004303j:plain



目次




実行環境


Windows10
Anaconda
VSCode
Python3.7

Androidスマホ
termux
Python3.7



動作確認
Windows10とAndroidスマホで確認しました。Pythonos.nameでOSを判断してブラウザ起動させておりますが、Android用はtermuxコマンドを使っている為それがインストールされていないと動きません。
また、os.nameはAndroidMacOSLinuxで同じposixという値を返すために、MacLinuxで実行するとエラーが出るかと思います(未確認、未対応)。



外部ライブラリの準備


bottleとmarkdownライブラリを使用しています。
pip install bottle markdownなどでインストールしPythonインタープリタimport bottle,markdownで確認しておきます。
bottleがno moduleと言われる場合は前回を参照ください。



作るもの


  1. プロジェクトフォルダ(これに以下のファイルを保存する)
  2. index.html(ホームページ。リンクのみ)
  3. input.html(入力用ページ)
  4. output.html(入力内容表示用ページ)
  5. main.py(実行用Pythonファイル)


(1)に(2)~(5)のファイルを保存して下さい。



実行方法と動作


実行はターミナルからコマンドで行います。

python main.py


  1. 実行すると入力内容保存用フォルダがプロジェクトフォルダ内に自動作成されます。
  2. ブラウザが起動しindex.htmlが開きます。リンクしか載ってません。リンクをクリックします。
  3. 入力用ページinput.htmlが開きます。markdownで適当に入力したら送信ボタンを押します。
  4. 入力内容はプロジェクトフォルダ内に自動作成したlog_folder/log.txtに保存されます。
  5. 入力内容をhtmlに変換し、同じく同フォルダ内のlog_html.txtに保存します。
  6. ブラウザがoutput.htmlを開き、入力テキストとhtmlに変換したテキストを表示します。
  7. backで先頭のホームページに戻ります。



(1)プロジェクトフォルダ作成


適当にフォルダを作ります。



(2)index.html作成


作ったプロジェクトフォルダ(1)に保存

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <title>ようこそ</title>
</head>

<body>
    <a href="http://localhost:8080/input">入力ページに移動</a>
</body>

</html>



(3)input.html作成


同じくプロジェクトフォルダ(1)に保存

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Input</title>
</head>

<body>
    <form action="output" method="post" accept-charset="UTF-8">
        <p>【投稿欄】<br>
            <textarea name="example1" id="example" cols="40" rows="8" wrap=off style="overflow:auto;"></textarea></p>
        <p><input type="submit" value="送信する"></p>
    </form>

    <br><br><br>
    <a href="http://localhost:8080/hello">back</a>
</body>

</html>



(4)output.html作成


同じく(1)フォルダに保存

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Output</title>
</head>
<body>
<pre>{{text}}</pre>
<pre>{{html_text}}</pre>
<br><br><br>
<a href= "http://localhost:8080/hello" >back</a>
</body>
</html>



(5)main.py作成


これも(1)フォルダに保存

from bottle import route, template, run, request
import os
import webbrowser
import subprocess
import markdown as md


# 投稿保存用フォルダ作成
def make_log_folder():
    os.makedirs("log_folder", exist_ok=True)


# ブラウザ起動
def open_browser():
    #url
    url = "http://localhost:8080/hello"
    # osの種類を取得
    which_os_name = os.name

    # osによってブラウザ起動制御
    if which_os_name == "nt": # windows
        webbrowser.open(url)
    elif which_os_name == "posix": # androidでtermuxをインストール済
        cmd = "termux-open-url " + url
        subprocess.run(cmd.split())


# ファイル出力する
#textで書き込み
def write_text_log(text):
        #textをlog_folderフォルダ内にファイル出力
        file_path_text = "./log_folder/log.txt"
        with open(file_path_text,"a",encoding="utf8",newline="\n" ) as f:
                f.writelines(text + "\n")

#markdownをhtmlに変換する
def conv_md_to_html(text):
        html_text = md.markdown(text)
        return html_text

#htmlを書き込み
def write_html_log(text):
        html_text = conv_md_to_html(text)
        file_path_html = "./log_folder/log_html.txt"
        with open(file_path_html,"a",encoding="utf8",newline="\n" ) as f:
                f.writelines(html_text + "\n")

##### 制御用 #####
# bottle API index.html
@route('/hello')
def hello():
        return template('index')
        
# bottle API input.html
@route('/input')
def input():
        return template('input.html') 

# bottle API output.html
@route('/output',method=["GET","POST"])
def output():
        # textを取得
        text = request.forms.example1
        #テキストで書き出す
        write_text_log(text)
        # markdownをhtmlに変換
        html_text = conv_md_to_html(text)
        # markdownをhtmlでファイル出力
        write_html_log(text)
        
        return template('output.html',text=text, html_text=html_text)


##### 実行 #####
make_log_folder()
open_browser()
run(host='localhost',port=8080,reloader=False,debug=True)



備考


index.html
特に必要ないページだが、ホームページにいきなり入力フォームがあるのも何だかな~と思って作った。
main.pydef hello()関数にある@route("/hello")で、html://localhost:8080/helloにアクセスするとreturnでindex.htmlが返って表示される。



index.html
<textarea>タグ内にwrap=off style="overflow:auto;を追加した。
これにより入力がフォームの幅より長くなっても自動でスクロールバーが付き、はみ出た部分が改行されない表示のまま入力可能になる。
wrap=offにしないと機能しない。
送信ボタンを押すと、<form>タグにあるaction="output" method="post"によりoutputページにPOSTで送られる。
main.pydef output()にある@route('/outputと、その最後のreturn template('output.html',text=text, html_text=html_text)がそれを行う。



output.html
上のinput.htmlmain.pydef output()により送られてきた値(textとhtml_text)を表示する。



main.py
入力内容保存フォルダを作ったりそれらをファイルに書き込んだりデータを各ページに送って表示させたりしている。
最終的に表示するdef output()@routeの所にだけmethod=["GET","POST"]は書けばよく、前回は勘違いしていた。



おわりに


classを使って書こうとしたがAttribute Errorが出まくって断念した(笑)
だが前回より若干理解が深まった。ポンコツでも役に立つ(2回目)



以上です。