Twitter連携のFlaskアプリを作ってデプロイ

この記事はFIXER Advent Calendar 2021( https://adventar.org/calendars/6837 )11日目の記事です。

前日の記事は「Azure Container AppsでVue.jsのアプリをデプロイする」でした。最近パブリックプレビューされたばかりのAzure Container Appsについての紹介記事でした。私もこれを読みながら触ってみたいと思います。

はじめに

みなさん、水は飲んでいますか?私はコーヒーやお茶ではなく、意識的によく水を飲むようにしています。

とある休日、ふと手元をみると愛用している水筒に”Hydro Flask”という文字。そういえば、(フレームワークの)Flaskを使ったことないなぁと思ったので、今回は飲んだ水の量をツイートするFlaskアプリを作ろうと思います!

愛用の水筒 Hydro Flask

やってみよう!!

今回はFlaskアプリデビューということで、ミニマム開発でいきたいと思います。そして、コード量自体は50行ほどとかなり少ないですが、Twiiter APIを使うためにはTwitter Developersに登録する必要があります。登録手順についてはこの記事で詳しく触れませんが、これが少し面倒で時間がかかります。

そこで、まずは「1. ローカルで動作するFlaskアプリの作成」、次に「2. Azure App Serviceにデプロイ」、最後に「3. Twitter認証連携をしてツイートできるアプリの作成」という3ステップで紹介したいと思います。

今回の構成

実行環境       :Mac OS / Python3
フレームワーク    :Flask
Twitter APIライブラリ :Tweepy
ホスティングサービス :Azure App Service

1. ローカルで動作するFlaskアプリの作成

まずはFlaskのチュートリアルを参考にしながら、ローカルで動作するFlaskアプリを作っていきましょう。アプリ用のフォルダを作成し、ターミナルでFlaskをインストールします。

$ pip3 install Flask

次にアプリケーションのメインとなるPyrhonファイルを作成します。のちのちですが、App Serviceはデフォルトで app.py という名前のファイルを実行するので、そうしておきましょう。

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

たったこれだけです。以下のコマンドを実行すると最初のFlaskアプリが起動して Hello, World! が表示されます!

$ export FLASK_APP=app
$ flask run
Hello World!

ただ、これだど少し寂しいので入力フォームを用意しましょう。app.py を入力フォームに体重を入力したら、1日に飲む推奨水分量を表示するようにしてみます。

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/", methods=['GET', 'POST'])
def index():
  if request.method == 'GET':
    return render_template("index.html")
  elif request.method == 'POST':
    weight = request.form["weight"]
    if weight:
      water = str(float(weight) * 35)
      return render_template('index.html', water = water)
    return render_template("index.html")

Pythonからの出力ではなくHTMLファイルを表示するときは render_template() を使うため、templatesフォルダを用意し、その下に index.html を作成します。

<!DOCTYPE html>
<html><body>
    <form action="/" method="post">
      体重[kg]:<input type="number" name="weight">
      <input type="submit" value="送信">
    </form>
    {% if water %}
      <p>1日に {{ water }}mL 飲みましょう!</p>
    {% endif %}
</body></html>

ポイントは app.py の @app.route("/", methods=['GET', 'POST']) ですね。ルートパスに各種メソッドの通信が来た時の動作を記述できるようにしています。そして index.html のフォームに送信ボタンを押した時のPOSTアクションと連携しています。

これを起動すると以下のようなアプリができました!

入力フォームを使ったFlaskアプリ

2. Azure App Service

次に先ほど作成したアプリをデプロイして、どこからでもアクセスできるようにしましょう。

App Service関連の記事は弊社ブログにもたくさんあるので、是非ご一読ください。

requirements.txt の作成

デプロイするときにはインポートするライブラリを requirements.txt に書いておく必要があるので、新たに作成します。バージョンをする場合はライブラリ名の後に記述します。最新版でいい場合はライブラリ名だけで大丈夫です。

Flask

準備ができたらソースコードをGitHubにアップロードしておきます。

Azure App Serviceの設定

ここからはAzure Portalでの手順です。(アカウントがない場合は作成してください)

リソースからApp Serviceを検索して作成してください。新規作成の手順で特に気をつける点はないと思いますが、今回はPythonを実行するアプリなので、ランタイムスタックは Python を選択してください。(プランは F1: Free でも動作します)

App Serviceが作成されたら、デプロイセンターからGitHubにあるリポジトリを選択してデプロイするソースコードを選択しましょう。

設定が完了したらURLをブラウザで開きましょう。もうデプロイできました!(カンタン)

App Serviceの作成

3. Twitter認証連携をしてツイートできるアプリの作成

最後はTwitter認証連携をしてツイートできるようにします。

Twitter Developersに登録

まずはTwitter APIを使用するために、Twitter Developersに登録する必要があります。どんな用途でどんなアプリを開発するかなどを記入して申請します。(私は申請して翌日くらいに承認されました)

承認されたらDeveloper PortalのProjects & Appsから新しいアプリを追加します。このときに生成される API KeyAPI Key SecretBearer Token をメモしておいてください。

また、設定項目の CALLBACK URLS にデプロイしているURLを、その他の必須項目になっているURLには適当なURLを入力してください。

Developer Portalの設定画面

Twitter認証とツイート機能の実装

先ほど作っていたアプリにTwitter認証とツイート機能を実装しましょう。まずはPythonでTwitter APIを使うライブラリのTweepyをインストールします。

$ pip3 install tweepy

また、requirements.txt にもtweepyを追記します。

Flask
tweepy

index.html に飲んだ水分量を入力できるフォームを追加します。

<!DOCTYPE html>
<html><body>
    {% if isAuthed %}
        <p>ログイン成功</p>
        <form method="post" action="/">
            「水を <input type="number" id="msg" name="msg"> mL飲んだ」
            <input type="submit" value="Tweet message!">
        </form>
    {% else %}
        <p>ログインしてください</p>
        <p><a href="{{ url_for('twitter_auth') }}">Twitter認証</a></p>
    {% endif %}
</body></html>

さらに app.py にも認証(OAuth 1.0a)とツイートの処理を書き足します。

import os
import tweepy
from flask import Flask, redirect, render_template, request
app = Flask(__name__)

CONSUMER_KEY = os.environ['CONSUMER_KEY'] # API Key
CONSUMER_SECRET = os.environ['CONSUMER_SECRET'] # API Key Secret
CALLBACK_URL = os.environ['CALLBACK_URL'] # デプロイURL
app.config['SECRET_KEY'] = os.urandom(24)
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET, CALLBACK_URL)

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        oauth_token = request.args.get('oauth_token', default = None, type=str)
        oauth_verifier = request.args.get('oauth_verifier',default = None, type=str)
        if oauth_token == None:
            return render_template("index.html", isAuthed = False)
        try:
            auth.request_token['oauth_token_secret'] = oauth_verifier
            auth.get_access_token(oauth_verifier)
        except Exception as e:
            return ''' <p>エラー</p> '''
        return render_template("index.html", isAuthed = True)
    elif request.method == 'POST':
        auth.set_access_token(auth.access_token, auth.access_token_secret)
        api = tweepy.API(auth)
        msg = "水を " + str(request.form["msg"]) + " mL飲んだ"
        api.update_status(msg)
        return render_template("index.html", isAuthed = True)

@app.route('/twitter_auth', methods=['GET'])
def twitter_auth():
    redirect_url = auth.get_authorization_url()
    return redirect(redirect_url)

このままではローカルで正常に動作しません。以下の os.environ['XXX'] の箇所に直接API Keyなどを記述すると動作します。ですが、GitHub上にそういったクレデンシャル情報はあげられません。( .env などを使う方法もありますが、この記事では触れません。)

CONSUMER_KEY = os.environ['CONSUMER_KEY'] # API Key
CONSUMER_SECRET = os.environ['CONSUMER_SECRET'] # API Key Secret
CALLBACK_URL = os.environ['CALLBACK_URL'] # デプロイURL

そこで、あらかじめApp Serviceに環境変数を登録しておき、その値を app.py から読み込むような書き方にしてあります。次にApp Serviceに環境変数を登録する手順を紹介します。

環境変数の設定

最後にApp Serviceにこれらの環境変数を登録します。Azure Portalのリソースにある「構成」からアプリケーション設定を追加しましょう。

App Serviceの環境変数設定画面

これが完了したら、Twitter連携もできました!

Twitter認証連携したアプリ画面

最後に

今回は思いつきでFlaskデビューしてみました。やってみるとメインで書いたPythoとHTMLを合わせてたった50行で自分でもびっくりしました。また、最近一般提供が始まったStatic Web AppsもApp Serviceの一部みたいなものなので、デプロイの勝手はほとんど同じです。こちらも是非使ってみたいですね。

参照