REST-APIを作ってみる(Python編)

2022/11/03

(最終更新: 2022/11/03

アイキャッチ画像

はじめに

REST-APIを作ってみるシリーズ第三弾。Node.jsは、ブログ作成や個人アプリ開発で少しばかり触れているが、会社で触れる機会のあるPythonとSpringBootも基礎だけ勉強したい。

ということで、基本的なAPIをPythonで作ってみます。Pythonは完全に初心者なので、基本的なところから始めています。

環境

  • 実行環境:Windows 11

Pythonインストール・初期設定

Pythonインストール

Pythonをインストールします。Anacondaというディストリビューションを利用すると、環境構築に必要なパッケージもまとめてインストールしてくれる。

Anaconda | Anaconda Distribution

統合開発環境(IDE)をインストール

VSCodeで開発しようと思っていたが、調べるとPyCharmという開発環境が無難なようなので、インストールする。

PyCharm:JetBrainsによるプロ開発者向けPython IDE

プロジェクトを作成する

「File」→「New Project」で好きな場所にプロジェクトを作成する。

プロジェクトを作成

試しに実行してみる

プロジェクトフォルダ直下にmain.pyというファイルを作成して、実行してみる。

メインファイルは以下のように書くのが決まり文句のようなものらしい。(main()関数の中に実行したい処理を書いて、main.pyを直接実行したときにのみ処理されるようにする。)

main.py
def main():
    print('ためしに実行してみる')


if __name__ == '__main__':
    main()

試しに実行してみる

モジュールをインストールする

Flaskを利用する

WEB APIを作成するためのモジュールが必要になるため、Flaskという有名なものを利用する。

https://pypi.org/project/Flask/

モジュールの使い方

Node.jsではnpm install [モジュール名]でインポートして、package.jsonを使ってプロジェクトごとに利用モジュールを管理できた。

一方で、Pythonの場合は、pipというコマンドを使ってインストールするようだが、基本はグローバルインストールになってしまう(該当マシンのpipが入るフォルダみたいなところにインストールされてしまう。npm -g installのようなもの?)ため、好ましくない。 適切に運用するには、プロジェクト単位の、pyenvなどを利用するなどして、仮想環境を構築してその中でpipインストールをする必要があるとのこと。

ただ、pipAnacondaは混ぜるな危険との話もあり、理解に時間がかかりそうなため今回は単純にPyCharmからモジュールを(グローバル)インストール。

PyCharmで「File」→「Settings」→「Project」→「Python Interpreter」→「+」

PyCharm

pip install flaskコマンドで実行しようとすると、ワーニングが発生してタイムアウトとなる。ちょっと理解しきれなかったので、モジュールのインストールに関しては、後日深堀りすることとする。

WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.

モジュールをインポート

main.pyの中でモジュールをインポートする。

main.py
from flask import Flask
# from パッケージ名 import モジュール(関数)

APIを作成する

Flaskの基本構文を利用してAPIを作成する。

基本的なAPI

localhost:5000にアクセスするとhelloという文字列を返却するAPIを作成する。 flaskのパッケージによって、アクセスされたURLによって、指示された値を返却するWEBサーバを作ることができる。

main.py
from flask import Flask

# appを宣言
app = Flask(__name__)
# appオブジェクトのrouteメソッドをデコレータとして記載
@app.route('/')def hello():
    return 'hello'


def main():
    app.debug = True
    app.run()

if __name__ == '__main__':
    main()  

実行したい関数を定義し、app.route()関数をデコレータにおくと、Webサーバのルーティングを実装することができる。

実行すると待ち受けが始まり、localhost:5000127.0.0.1:5000と同じ)にアクセスすると、helloという値が返ってくる。

ブラウザ

PowerShellを使ってヘッダなどを見てみると以下の通り。

> curl http://127.0.0.1:5000/                                                                          

StatusCode        : 200
StatusDescription : OK
Content           : hello
RawContent        : HTTP/1.0 200 OK
                    Content-Length: 5
                    Content-Type: text/html; charset=utf-8
                    Date: Sun, 13 Nov 2022 06:57:17 GMT
                    Server: Werkzeug/2.0.3 Python/3.9.12

                    hello
Forms             : {}
Headers           : {[Content-Length, 5], [Content-Type, text/html; charset=utf-8], [Date, Sun, 13 Nov 2022 06:57:17 GM
                    T], [Server, Werkzeug/2.0.3 Python/3.9.12]}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 5

メンバーリストを返却するAPIを作成

仕様とソースコード

メンバーのリストを照会できるAPIを作成する。

  • パスパラメータ無しでアクセスされた場合、全メンバーの情報をリスト返却する。
  • パスパラメータにメンバーIDを入れた場合、該当するメンバーIDの情報を返却する。

他のREST-APIを作ってみるシリーズと同じような仕様。

REST-APIを作ってみる(Node.js + Express編)

REST-APIを作ってみる(Node.js + Express編)

REST-APIを作ってみるシリーズ第一弾。Node.js+ExpressでAPIを作成する。

https://bunsugi.com/rest-api-node-express-2
main.py
import json  # 辞書型をJSON型に変換するために利用(標準パッケージ)

from flask import Flask

# appを宣言
app = Flask(__name__)

# 辞書型の配列
members = [
    {"id": "1", "name": "Taro", "team": "A"},
    {"id": "2", "name": "Jiro", "team": "B"},
    {"id": "3", "name": "Saburo", "team": "A"}]


# パスパラメータがない場合
@app.route('/')
def root():
    ## 辞書型はそのまま返却できないのでjson.dumpsを使う
    return json.dumps(members)


# パスパラメータがある場合
@app.route('/<member_id>')
def filter_by_id(member_id):
    member = list(filter(lambda member: member['id'] == member_id, members))
    return json.dumps(member)


def main():
    app.debug = True
    # hostとportを指定したいときは以下の記載とする。
    app.run(host='127.0.0.1', port=5000)


if __name__ == '__main__':
    main()

パスパラメータがない場合の解説

まず、membersはPythonの辞書型の配列で定義する。

members = [
    {"id": "1", "name": "Taro", "team": "A"},
    {"id": "2", "name": "Jiro", "team": "B"},
    {"id": "3", "name": "Saburo", "team": "A"}]

ルートURLにアクセスした際は、membersをすべて返却するが、APIの返却は辞書型では不可であるためJSONに変換する。

def root():
    return json.dumps(members)

ちなみにreturn membersのように返却すると、アクセス時に以下のエラーが発生する。

TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a list.

パスパラメータがある場合の解説

localhost:5000/1とアクセスした場合にid=1のメンバー情報のみ返却されるようにする。

パスパラメータを利用したい場合は以下のように変数を設定する。

@app.route('/<member_id>')

すると、以下の関数でmember_idが変数として利用できる。

members配列の中で、idmember_idと一致する行のみ抜き出すため以下の処理を記載する。

def filter_by_id(member_id):
    member = list(filter(lambda member: member['id'] == member_id, members))

filter関数は、第一引数に関数、第二引数に配列を取り、配列の要素1個1個を見て、関数を実行し、trueとなる要素のみ返却する。

filter(関数, 配列)

関数部分には、Node.jsでもおなじみのラムダ式(無名関数)を利用する。Pythonでの書き方は以下のようになる。

lambda 引数: 返却値

filter関数は、Generate Objectと呼ばれる直接読めない値を返却する。リストにするにはlist関数ではさんで上げる必要がある。

list(filter(関数, 配列))

動作確認結果

パスパラメータがない場合

パスパラメータがない場合

> curl http://127.0.0.1:5000


StatusCode        : 200
StatusDescription : OK
Content           : [{"id": "1", "name": "Taro", "team": "A"}, {"id": "2", "name": "Jiro", "team": "B"}, {"id": "3", "n
                    ame": "Saburo", "team": "A"}]
RawContent        : HTTP/1.0 200 OK
                    Content-Length: 128
                    Content-Type: text/html; charset=utf-8
                    Date: Sun, 13 Nov 2022 07:31:32 GMT
                    Server: Werkzeug/2.0.3 Python/3.9.12

                    [{"id": "1", "name": "Taro", "team": "A"}, {"...
Forms             : {}
Headers           : {[Content-Length, 128], [Content-Type, text/html; charset=utf-8], [Date, Sun, 13 Nov 2022 07:31:32
                    GMT], [Server, Werkzeug/2.0.3 Python/3.9.12]}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 128

パスパラメータがある場合

パスパラメータがある場合

> curl http://127.0.0.1:5000/2


StatusCode        : 200
StatusDescription : OK
Content           : [{"id": "2", "name": "Jiro", "team": "B"}]
RawContent        : HTTP/1.0 200 OK
                    Content-Length: 42
                    Content-Type: text/html; charset=utf-8
                    Date: Sun, 13 Nov 2022 07:32:00 GMT
                    Server: Werkzeug/2.0.3 Python/3.9.12

                    [{"id": "2", "name": "Jiro", "team": "B"}]
Forms             : {}
Headers           : {[Content-Length, 42], [Content-Type, text/html; charset=utf-8], [Date, Sun, 13 Nov 2022 07:32:00 G
                    MT], [Server, Werkzeug/2.0.3 Python/3.9.12]}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 42

※存在しないidを指定した場合の処理は未実装のためインデックスエラーが出る。 インデックスエラー

おわりに

PythonでAPIを作成することができた。

こまごましたところでバックエンドの知識が足りないので、本当はちゃんと学ばないと。。 Pythonは、スクレイピングで利用したいので、癖があるものの基本は身に着けたい。

関連記事

残課題

  • デプロイ
  • メソッド(POST等)を変える場合
  • DBとの接続
  • テンプレートエンジン(余裕があれば)


個別連絡はこちらへ→Twitterお問い合わせ

プロフィール

プロフィールイメージ

はち子

事業会社のシステム部門で働きはじめて5年目の会社員。システム企画/要件定義/システムアーキテクチャ等。

Twitter→@bun_sugi

過去の記事について

はてなブログに掲載の記事(主にプログラミングメモ)についてはこちらに掲載しております。(本ブログに移行中)

タグ一覧

関連記事

Copyright© 2024, エンジニアを目指す日常ブログ

お問い合わせ|プライバシーポリシー