ビッグデータに基づいた研究開発やビジネスは現代において珍しくありません。そんな中重要視されるのが、簡単にデータを取得できる仕組み【WebAPI】です。WebAPI(Web Application Programming Interface)はプログラムによってWeb上のデータベースから容易にデータを取得できるインターフェースです。データ解析と親和性の高いプログラミング言語であるPythonを使うことで簡単にWebAPIを自前で実装することもいまや可能になっています。本記事ではWebAPIの実装方法の紹介と、分析対象であるデータをcsv形式で描画・取得するためのGUIについて紹介します。
ソースコードはすべて以下のレポジトリに配置してあります。参考までにどうぞ。
github.com
WebAPIで公開する仮データの準備
はじめにwebAPIに登録するためのデータベースの準備を行います。まぁデータベースとはいっても、関係データベース(RDBMS)のような高等なものではなく1つのテーブルデータです。
ID | 名前 | 年齢 | タイプ |
---|---|---|---|
0 | ポンタ | 5 | ウスリータヌキ |
1 | たぬまる | 3 | タイリクタヌキ |
2 | たぬ子 | 8 | エゾタヌキ |
主キーをID, 名前、年齢、タイプをその従属キーとします。このようなデータを登録します。
データベースの作成は、Pythonでデータベース構築が行えるSQLAlchemyを用います。python create_db.pyの実行によりデータベースが作成されます(データは未登録)。
SQLAlchemy - The Database Toolkit for Python
#setting.py from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.ext.declarative import declarative_base DATABASE = 'sqlite:///tanuki.sqlite3' #Engine(PythonをSQLに変換)を定義 ENGINE = create_engine( DATABASE, encoding = "utf-8", echo=True ) SessionClass=sessionmaker(ENGINE) #セッションを作るクラスを作成 session=SessionClass() Base=declarative_base()
#create_db.py import sys from sqlalchemy import Column, Integer, String, Float, ForeignKey from sqlalchemy.orm import relationship from setting import Base from setting import ENGINE class Tanuki(Base): __tablename__="Tanuki" id=Column(Integer, primary_key=True) name=Column(String(255)) age=Column(Integer) type=Column(String(255)) def main(args): Base.metadata.create_all(bind=ENGINE) if __name__ == "__main__": main(sys.argv)
データベースにデータを登録するには、レポジトリのregister_tanuki.pyを実行します。ここの説明は省略します。
WebAPIの実装
WebAPIをPythonで実装するには、Django, Flask, FastAPIあたりが有力な選択肢になってくるかと思います。Djangoはフルスタックフレームワークなので小規模サービスを作るには重すぎる。Flaskはより軽量で広く使われているサービス向けですが、今回は後発の軽量WebAPIであるFastAPIを使うことにしました。最近は日本語の記事もかなり増えている印象です。ここでは実装方法の詳細は省略しますが、中核となる部分の実装だけ紹介します。
デフォルトではjson形式を返しますが、ストリーム形式のデータを取得することも可能です(PandasのDataFrameを直接的には返すことはできない模様)。なので、Pythonの標準パッケージであるioモジュールによってあらかじめ作っておいたDataFrame形式のデータをストリーム形式に変換します。それをFastAPIのレスポンス機能であるStreamingResponseに渡すことでAPIの戻り値として設定できるようになります。
#fast.py SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=ENGINE) # Dependency def get_db(): db = SessionLocal() try: yield db finally: db.close() app = FastAPI() """ tanuki API """ @app.get("/v1/tanuki/", tags=["Tanuki"]) async def read_tanuki(db:Session = Depends(get_db)): #DataFrameは事前に作成しておく(別にDFを介す必然性はないと思う)。 tanuki = get_tanuki(db) df = dumpTanuki(tanuki) #DFをストリーム形式のデータに変換 stream = io.StringIO() df.to_csv(stream, index = False) #stream.getvalue()によってデータを取り出し response = StreamingResponse(iter([stream.getvalue()]), media_type="text/csv") response.headers["Content-Disposition"] = "attachment; filename=export.csv" return response
FastAPIの実装が完了したら、以下のコマンドでウェブサーバーを立ち上げます。
uvicorn fast:app --reload
ウェブページにアクセスし、ポチポチやっていると以下の画面に遷移するかと思います。これでDownload fileをクリックするとcsvファイルをダウンロード可能です。また、データ取得用のURLやcurlでデータを取得するためのコマンドも記載されています。これはAPI使用方法に関するドキュメントぺージですが、これが自動的に生成されるのはうれしいです。
GUIの実装
APIとしての機能だけでなく、データをより見やすい形で表示することも可能です。この場合は別のページにルーティングして、そこにGUI用のhtmlを表示できるような設定にします。先ほどのFastAPIの設定プログラム(fast.py)に以下を追記します。やっていることとしては、cssの置き場所であるstaticフォルダをウェブサーバー上にマウントします。またPythonからhtmlを扱うためのフレームワークであるJinja2TemplatesをFastAPIの機能から使用可能にします。Jinja2を介して、テーブルデータを表示するためのhtmlとデータベースのデータを引数に設定します。これにより、http://127.0.0.1:8000/tableのURLにテーブルが表示されるようになります。
app.mount("/static", StaticFiles(directory="static"), name="static") templates = Jinja2Templates(directory="templates") jinja_env = templates.env async def table(request: Request, db:Session = Depends(get_db)): tanuki = get_tanuki(db) return templates.TemplateResponse('index.html', {'request': request, "tanuki": tanuki}) app.add_api_route('/table', table)
以下が表示されるページです。htmlとcssの内容説明は省略します。また、csv形式でデータを取得するためのダウンロードボタンを付けました。このボタンクリック時の参照先は、先ほど設定したAPIのcsvファイルの置き場所を設定すれば良いので意外と簡単に実装できました。とてもうれしい。
終わりに
SQLAlchemy, FastAPI, sqlite3, html, cssと色々使っているのでそれぞれの説明は超絶省略せざるを得ませんでした。。。詳細な使い方は公式ドキュメントやStackoverflowを当たれば必ず出てきますのでご参照下さい。今回の記事が誰かにとって、FastAPIでこんなことが出来るんだ!というきっかけになれば幸いです。FastAPIを使えば1つのフレームワークでAPIやGUIをかなり高い自由度で管理でき、かつ使い方もシンプルなので個人的にはかなりおすすめです。大規模データを登録する場合には、細かく作りこむことでユーザーフレンドリーなサービスの展開も可能かもしれません。