フロントど素人がPython3で英単語分割アプリを作ってみた

# 作ったもの🤖
Flaskのフレームワークを使って、Python初心者が英単語の分割Webアプリケーションを作ってみました👾
以下のように、英文をを入れたら単語数の大きい順に結果を返してくれます！

**1. カメラOCRでテキスト文字認識
2. 文字をアプリに投入して、頻出順に並び替えする
3. よく出る単語が分かる**

というような用途を想定して作成しました！
これで英語の長文読解が捗るはず...！

![ezgif.com-video-to-gif (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274358/350895c8-9bfb-b2a1-7967-ff2572030818.gif)


# Flask概要
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274358/0a3f2f9c-b0de-9872-0a18-56ca463a833f.png)

Djangoと比べて軽量に設計されているWebフレームワーク。
Djangoは、```テンプレートの豊富さ```, ```管理画面```, ```パスワード認証```, ```セキュリティへの考慮```が特徴だそうだけど、
プロトタイプで動作検証程度だったら、たぶん軽量の```Flask```でよさそう？

## 特徴
- とにかくシンプルで最低限の機能しか用意されていない。それがいい
- 別途必要な機能はプラグインとして用意されている
- アーキテクチャをシンプルに保ちながら拡張性が高く、自分の望む仕組みにできることが魅力

## マイクロフレームワーク

「Flask」はマイクロフレームワークと呼ばれていて、必要最低限の機能を備えてます。
必要最低限ということは、それ以外は拡張機能で補う必要があるということです。
個人的には必要になったら追加するほうが合理的だと思うけど、実運用したことはないので、機能追加が面倒でない人ならマイクロスタートで```Flask```を使ってもいいのかも？

# コード基本概念

## モジュールのインポートとインスタンス

- importで```flask```からFlaskクラスをimportします
- ```app = Flask(__name__)```で自身のインスタンスを作ります
    - Pythonでは「__name__」変数の値は、Pythonモジュールが「スクリプトとして実行されている」か「他のコードでモジュールとして読み込まれているか」に応じて異なる。

```python
from flask import Flask
app = Flask(__name__)
```

## ルーティング情報

ルーティングとは、URLと処理を対応づけることです。Flaskのルーティングは、URLとfunctionを紐づけ、ルーティングには```route()```を用います。

下記の例では、```@app.route('/')```とすることで、root階層にアクセスがあった際に```Hello World!!```と返すようになります。
すごく簡単ですが、これがルーティングの設定概念。

```python
from flask import Flask
app = Flask(__name__)

@app.route('/')
    def HelloWorld():
        return "Hello World!!"
```


# 実装したコード
## ディレクトリ構造

- CSSやJSなどのファイルはディレクトリ規則があり、.pyファイルと同階層の```static```ディレクトリ内に入れておく必要があります。
- HTMLの静的ファイルは```templates```ディレクトリに置く必要があります。

※注意：sqliteとjsファイルは使っていません



```bash:bash
.
├── database.sqlite
├── main.py
├── static
│   ├── js
│   │   └── main.js
│   └── style.css
└── templates
    ├── index.html
    └── main.html
```

## ポイント
- ```app.run(debug=True, host='0.0.0.0', port=8877)```
    - debug=True:デバッグモードを有効化(==すぐに変更が反映される)
    - port8877を指定してローカルホストで起動
- ```replace```メソッドで単語に邪魔な記号(!?,.\n())を排除
- 空白じゃない場合```if x != " ":```、配列に格納して文字列を整形している
- 単語は```lower関数```ですべて小文字化
- Pandasのデータフレームを利用して結果を返している
    - 取得結果をSeriesに一旦格納 -> DataFrame化
- HTMLに結果を渡すためには、```text=text```だったり、```total=total```とpythonに渡された値を```render_template```で```指定したHMTL```に渡す必要がある
    - ```return render_template("index.html", text=text, total=total.to_html())```
    - htmlにPandasのデータフレームを渡すためには、関数に入ってきた結果（ここでは"total"）```.to_html()```とする必要がある

```python:python3
from flask import Flask, render_template, session, request
import pandas as pd
import numpy as np

app = Flask(__name__)

#conn = sqlite3.connect('database.sqlite')
#c = conn.cursor()

def count_word(text):
    txts = text
    result = []
    tmp = ""
    dic = {}
    
    for x in txts:
        if x != " ":
            tmp += x
        else:
            chikan = tmp.replace('\n', '').replace('(', '').\
            replace(')', '').replace('[', '').replace(']', '').\
            replace('.', '').replace(',', '').replace(':', '').\
            replace('-', '').replace('?','').replace('!','')
            
            result.append(chikan.lower())
            tmp = ""
            
    for word in result:
        if not word in dic:
            dic[word] = 1
        else:
            dic[word] += 1
    
    s = pd.Series(dic)
    df = pd.DataFrame(s, columns=['単語数'])
    dfs = df.sort_values(by='単語数', ascending=False) 
    return dfs


@app.route("/", methods=["GET","POST"])
def main_page():
    total = ''

    if request.method == 'POST':
        text = request.form['text']
        total = count_word(text)
        return render_template("index.html", text=text, total=total.to_html())
    
    return render_template("index.html", total=total)

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=8877)
```

## 一応解説書きました。見づらくてすみません
我ながら見づらい...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274358/1f232aa9-6306-8080-dd17-e75c2166d491.png)


## HTML
普通のHTMLです。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <link rel="stylesheet" href="/static/style.css">
        <title>Split English Sentences</title>  
    </head>
    <body>
        <div class="body2">
        <h1>Split English Sentences</h1>
        <h4>Please input your sentence below</h4>
        <form action="/" method="POST">
            <div class="form1">
                <label for="name">SENTENCE</lable><br>
                <textarea name="text">{{ text }}</textarea><br><br>
                <input type="submit" value="SUBMIT" class="sub_button"></input>
                &nbsp;&nbsp;&nbsp;&nbsp;
                <input type="reset" value="RESET" class="reset_button"></input>
            </div>
        </form>
        <br><br>
        <div class="result">
        <span><font size="4"><strong>The English sentence you've input</strong></font></span><br><br>
        <font>{{ text }} </font>
        </div>
        <br><br>
        <h4>RESULT</h4><br><br>
        <tr text-align="center">{{ total | safe }}</tr>
        <!--<script src="/static/js/main.js"></script>-->
        </div>
    </body>
    <footer>
    <p>COPYRIGHT &#169; TNOCE.COM ALL RIGHTS RESERVED.	</p>
    </footer>
            
</html>
```

## CSS

なんかCSSのサイトでおしゃれアイコンをコピー気がする🙃

```css:style.css
@charset 'utf-8';

h1 {
  position: relative;
  padding: 0.5em;
  background: #5d87fd;
  color: white;
}

h1::before {
  position: absolute;
  content: '';
  top: 100%;
  left: 0;
  border: none;
  border-bottom: solid 15px transparent;
  border-right: solid 20px rgb(149, 158, 155);
}

h4 {
  color: #1d51df;
  text-shadow: 0 0 5px white;
  border-left: solid 7px #010079;
  background: -webkit-repeating-linear-gradient(-45deg, #cce7ff, #cce7ff 3px,#e9f4ff 3px, #e9f4ff 7px);
  background: repeating-linear-gradient(-45deg, #cce7ff, #cce7ff 3px,#e9f4ff 3px, #e9f4ff 7px);
}

body {
  text-align: center;
  padding: 20px;
  font-size: 20px;
}

.body2 {
  background-color: white;
  padding: 20px;
}

textarea {
  resize: none;
  width: 70em;
  size: 20px;
  height: 20em;
  row-gap: 20px;
  box-shadow: 0 0 7px #8e9091;
  border: 1px solid #3c3d3d;
}

.result {
  background: #eeeeee;
  width         : 50%;
  display       : inline-block;
  border-radius : 10%;
  font-size     : 10pt;
  text-align    : center;
  padding       : 20px;
}
  
.sub_button {
  display       : inline-block;
  border-radius : 5%;          /* 角丸       */
  font-size     : 16pt;        /* 文字サイズ */
  text-align    : center;      /* 文字位置   */
  cursor        : pointer;     /* カーソル   */
  padding       : 12px 12px;   /* 余白       */
  background    : #999999;     /* 背景色     */
  color         : #ffffff;     /* 文字色     */
  line-height   : 1em;         /* 1行の高さ  */
  transition    : .3s;         /* なめらか変化 */
  box-shadow    : 6px 6px 3px #666666;  /* 影の設定 */
  border        : 2px solid #999999;    /* 枠の指定 */
}
  
.sub_button:hover {
  box-shadow    : none;        /* カーソル時の影消去 */
  color         : #999999;     /* 背景色     */
  background    : #ffffff;     /* 文字色     */
}

.reset_button {
  display       : inline-block;
  border-radius : 5%;          /* 角丸       */
  font-size     : 16pt;        /* 文字サイズ */
  text-align    : center;      /* 文字位置   */
  cursor        : pointer;     /* カーソル   */
  padding       : 12px 12px;   /* 余白       */
  background    : #999999;     /* 背景色     */
  color         : #ffffff;     /* 文字色     */
  line-height   : 1em;         /* 1行の高さ  */
  transition    : .3s;         /* なめらか変化 */
  box-shadow    : 6px 6px 3px #666666;  /* 影の設定 */
  border        : 2px solid #999999;    /* 枠の指定 */
}

.reset_button:hover {
  box-shadow    : none;        /* カーソル時の影消去 */
  color         : #999999;     /* 背景色     */
  background    : #ffffff;     /* 文字色     */
}

table{
  border-collapse: collapse;
  border-spacing: 10;
  width: 50%;
  font-size: 20px;
  margin-left: auto;
  margin-right: auto;
}

table tr{
  border-bottom: solid 1px #eee;
  cursor: pointer;
}

table tr:hover{
  background-color: #d4f0fd;
}

table th,table td{
  text-align: center;
  width: 25%;
  padding: 15px 0;
}
```

## 次は
ちゃんとデータベースに格納されるように改修します🙃