Flask を使ってみました

Flask を使ってみたので、メモです。

Installing 1

Installing

Install and update using pip:

pip install -U Flask

GitHub - pallets/flask: The Python micro framework for building web applications.

この通りにインストールしました。

$ pip install -U Flask
Collecting Flask
  Downloading https://files.pythonhosted.org/packages/9b/93/628509b8d5dc749656a9641f4caf13540e2cdec85276964ff8f43bbb1d3b/Flask-1.1.1-py2.py3-none-any.whl (94kB)
    100% |████████████████████████████████| 102kB 691kB/s
Collecting Werkzeug>=0.15 (from Flask)
  Downloading https://files.pythonhosted.org/packages/d1/ab/d3bed6b92042622d24decc7aadc8877badf18aeca1571045840ad4956d3f/Werkzeug-0.15.5-py2.py3-none-any.whl (328kB)
    100% |████████████████████████████████| 337kB 1.3MB/s
Collecting itsdangerous>=0.24 (from Flask)
  Downloading https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
Requirement already satisfied, skipping upgrade: click>=5.1 in /usr/lib/python3/dist-packages (from Flask) (6.7)
Collecting Jinja2>=2.10.1 (from Flask)
  Downloading https://files.pythonhosted.org/packages/1d/e7/fd8b501e7a6dfe492a433deb7b9d833d39ca74916fa8bc63dd1a4947a671/Jinja2-2.10.1-py2.py3-none-any.whl (124kB)
    100% |████████████████████████████████| 133kB 1.9MB/s
Requirement already satisfied, skipping upgrade: MarkupSafe>=0.23 in /usr/lib/python3/dist-packages (from Jinja2>=2.10.1->Flask) (1.0)
Installing collected packages: Werkzeug, itsdangerous, Jinja2, Flask
Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/usr/local/lib/python3.6/dist-packages/Werkzeug-0.15.5.dist-info'
Consider using the `--user` option or check the permissions.

You are using pip version 18.1, however version 19.2.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

エラーになりました。

そういえば、普段 --user オプションをつけていました。

Installing 2

改めて --user オプションをつけてインストールしました。

$ pip install -U Flask --user
Collecting Flask
  Using cached https://files.pythonhosted.org/packages/9b/93/628509b8d5dc749656a9641f4caf13540e2cdec85276964ff8f43bbb1d3b/Flask-1.1.1-py2.py3-none-any.whl
Collecting itsdangerous>=0.24 (from Flask)
  Using cached https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
Requirement already satisfied, skipping upgrade: click>=5.1 in /usr/lib/python3/dist-packages (from Flask) (6.7)
Collecting Jinja2>=2.10.1 (from Flask)
  Using cached https://files.pythonhosted.org/packages/1d/e7/fd8b501e7a6dfe492a433deb7b9d833d39ca74916fa8bc63dd1a4947a671/Jinja2-2.10.1-py2.py3-none-any.whl
Collecting Werkzeug>=0.15 (from Flask)
  Using cached https://files.pythonhosted.org/packages/d1/ab/d3bed6b92042622d24decc7aadc8877badf18aeca1571045840ad4956d3f/Werkzeug-0.15.5-py2.py3-none-any.whl
Requirement already satisfied, skipping upgrade: MarkupSafe>=0.23 in /usr/lib/python3/dist-packages (from Jinja2>=2.10.1->Flask) (1.0)
Installing collected packages: itsdangerous, Jinja2, Werkzeug, Flask
Successfully installed Flask-1.1.1 Jinja2-2.10.1 Werkzeug-0.15.5 itsdangerous-1.1.0
You are using pip version 18.1, however version 19.2.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

インストールできました。

A Simple Example

次のようなサンプルのファイルを作りました。

# hello.py
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello, World!"

次のように実行しました。 FLASK_APP を設定するみたいです。

$ env FLASK_APP=hello.py flask run
 * Serving Flask app "hello.py"
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Example 2

株式のヒストリカルデータをダウンロードするサンプルを作りました。

pip で numpy, pandas, requests をインストールしてあります。

# historical-prices.py
import datetime
import io
import numpy as np
import pandas as pd
import os
import requests
from flask import Flask, request, send_from_directory

app = Flask(__name__)

@app.route("/")
def root():
    title = 'Historical Prices'
    res = f'''
        <!DOCTYPE html>
        <html>
            <head>
                <title>{title}</title>
                <meta charset="utf-8" />
            </head>
            <body>
                <h1>{title}</h1>
                <ul>
                    <li><a href="/download?symbol=DJIA">Dow Jones Industrial Average (DJIA)</a></li>
                    <li><a href="/download?symbol=SPX">S&P 500 Index (SPX)</a></li>
                    <li><a href="/download?symbol=COMP">NASDAQ Composite Index (COMP)</a></li>
                    <li><a href="/download?symbol=NIK">NIKKEI 225 Index (NIK)</a></li>
                    <li><a href="/download?symbol=180460">TOPIX Index (180460)</a></li>
                    <li><a href="/download?symbol=SHCOMP">Shanghai Composite Index (SHCOMP)</a></li>
                    <li><a href="/download?symbol=HSI">Hang Seng Index (HSI)</a></li>
                    <li><a href="/download?symbol=UKX">FTSE 100 Index (UKX)</a></li>
                    <li><a href="/download?symbol=DAX">DAX (DAX)</a></li>
                </ul>
            </body>
        </html>
    '''
    return res

@app.route("/download")
def download():
    symbol = request.args.get('symbol')
    text = get(symbol)
    df = to_csv(symbol, text)
    res = send_from_directory(os.getcwd(), f'{symbol}.csv', as_attachment=True, attachment_filename=f'{symbol}.csv')
    os.remove(f'{symbol}.csv')
    return res

def get(symbol):
    url_dict = {
        'DJIA': 'https://quotes.wsj.com/index/DJIA/historical-prices/download',
        'SPX': 'https://quotes.wsj.com/index/SPX/historical-prices/download',
        'COMP': 'https://quotes.wsj.com/index/COMP/historical-prices/download',
        'NIK': 'https://quotes.wsj.com/index/JP/XTKS/NIK/historical-prices/download',
        '180460': 'https://quotes.wsj.com/index/JP/TOKYO EXCHANGE (TOPIX)/180460/historical-prices/download',
        'SHCOMP': 'https://quotes.wsj.com/index/CN/XSHG/SHCOMP/historical-prices/download',
        'HSI': 'https://quotes.wsj.com/index/HK/XHKG/HSI/historical-prices/download',
        'UKX': 'https://quotes.wsj.com/index/UK/FTSE%20UK/UKX/historical-prices/download',
        'DAX': 'https://quotes.wsj.com/index/DX/XETR/DAX/historical-prices/download',
    }
    start_dict = {
        'DJIA': datetime.date(1970, 12, 31),
        'SPX': datetime.date(1978, 1, 3),
        'COMP': datetime.date(1971, 2, 5),
        'NIK': datetime.date(1949, 5, 16),
        '180460': datetime.date(2008, 12, 30),
        'SHCOMP': datetime.date(1992, 5, 8),
        'HSI': datetime.date(1985, 1, 2),
        'UKX': datetime.date(1985, 12, 31),
        'DAX': datetime.date(1987, 12, 30),
    }
    url = url_dict[symbol]
    start = start_dict[symbol]
    end = datetime.date.today()
    days = (end - start).days
    payload = {
        'MOD_VIEW': 'page',
        'num_rows': days,
        'range_days': days,
        'startDate': start.strftime("%m/%d/%Y"),
        'endDate': end.strftime("%m/%d/%Y"),
    }
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0',
        'Accept': 'text/html, */*; q=0.01',
        'Accept-Language': 'ja,en-US;q=0.7,en;q=0.3',
        'Referer': url.replace('/download', ''),
        'Connection': 'keep-alive',
        'TE': 'Trailers',
    }
    r = requests.get(url, params=payload, headers=headers, timeout=60)
    r.raise_for_status()
    r.text
    return r.text

def to_csv(symbol, text):
    df = pd.read_csv(io.StringIO(text), index_col=0, parse_dates=True)
    df2 = df.sort_index()
    df2.to_csv(f'{symbol}.csv')
    return df2

これを次のように実行しました。

$ env FLASK_APP=historical-prices.py flask run
 * Serving Flask app "historical-prices.py"
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

http://127.0.0.1:5000/ にアクセスすると。

ちゃんとダウンロードできたみたいでした。

終わり

Flask は Node.js の Express のような印象を受けました。

参考