Web APIとパラメータ (P)

はじめに

Web API へのアクセスも、基本 URL を作ってアクセスし、出力結果を Stream として取り出すことはできます。

Java ではRetrofit が有名なんですが、Python にも Retrofit に影響をうけたUplinkがあるので、使ってみます。まだ、version が 1.0 未満みたいなんですが。

機能としては、

  • Web API の引数の処理を簡単に記述可能
    • API 引数の文字列に、空白とか、日本語とか入れるときの変換法とか調べるの嫌だよね?
  • 取得結果の JSON を dict としてあつかうこともできるし、pydantic などをつかって class に変換することもできる

詳しいことに興味がある人は、Uplink quickstartを読むとよいでしょう。

利用プログラム例

Web API を Java API に

以前お伝えしたように、 Web API 側は、URL を受け取って、結果を返す関数のようなものです。 でも、引数の与え方などが特殊なので、プログラムからメソッド気分で呼ぶのは大変ですよね。

上記チュートリアルの例をそのまま使いますが(ユーザ名だけ octcat から plham に変更しています)、例えば、github Contents API の場合、

にアクセスすることで、plham というユーザの repository 情報をリストの形で返してくれます。plham の部分がパラメータですね。

[
  {
    "id": 54699962,
    "node_id": "MDEwOlJlcG9zaXRvcnk1NDY5OTk2Mg==",
    "name": "plham",
    ...
  },
  {
    "id": 54700048,
    "node_id": "MDEwOlJlcG9zaXRvcnk1NDcwMDA0OA==",
    "name": "plham.github.io",
    ...
  },
  ...
}

これを、python から簡単にアクセスできるように、

   response = github.get_repos("plham")

のようなメソッド呼び出しで取得できるようにします。

そのためには、

  • BaseURL が https://api.github.com であり、
  • get_repos(username) に応じて、 users/{username}/repos という path にアクセスし({username} が引数 usernameの値で置き換わる)、

ことを指定しないといけません。pydantic を使う場合は、さらに

  • 結果の JSON をどんな python クラスに変換するか

も指定します。

API の呼出し方を指定

まずは dict で受け取るバージョンで説明します。

class GitHub(Consumer):
    """A Python Client for the GitHub API."""

    @returns.json()  # 結果の JSON を dict や list に変換して受け取り
    @get("users/{username}/repos")  # URL への変換方法
    def get_repos(self, username):
        """Get user's public repositories."""

BaseURL の指定は、GitHub クラス生成時に指定します。

    github = GitHub(base_url="https://api.github.com/")
    lst = github.get_repos("plham")  # 結果の受け取り

これで、結果を取得して、中身の JSON 相当のデータ構造(今回は dict ではなく dict の list)を取得できます。

URL encoding

Web API の GET メソッドを呼び出すときは、URL の形で引数が渡されます。 ただ、URL 中には日本語とか書けませんし、space などもかけません。

こういう場合は、引数を URL encoding という形式に変換して送ることになっています。 皆さんも、Web browser の URL 欄には、

https://www.google.com/search?q=甲南大学

とか書いてあるのに、copy & paste すると

https://www.google.com/search?q=%E7%94%B2%E5%8D%97%E5%A4%A7%E5%AD%A6

って変わる経験があるんじゃないでしょうか。

URL encoding を直接おこなうには、python では urllib.parse などが利用できますが、 uplink も対応してくれていますのでご安心を。

python クラスへの変換

pydantic をつかって dict から python クラスのオブジェクトに変換してもいいんですが、もっと Web API を関数っぽく書くこともできます。

# 先に型定義。 Java 版にそろえています
class Repo(BaseModel):
    id: int    # 必須
    html_url: HttpUrl  # 必須
    description: Optional[str]  # なくてもよい
    name: str   # 必須

# Web API 定義
class GitHub(Consumer):
    @returns.json()
    @get("users/{username}/repos")
    def get_repos(self, username) -> List[Repo]:
        """Get user's public repositories."""

Web API 定義側で返り値の型 (Type Hint) を指定しているところがポイントですね。

これで、呼び出した結果は Repos の list として扱えます。

    github = GitHub(base_url="https://api.github.com/")
    repos = github.get_repos("plham")  # repos は Repo の list
    print(repos)
    print("repos[0].html_url", repos[0].html_url)

非同期処理は?

uplink では、ネットワーク呼出しは同期呼出しといって、結果が返ってくるまで待つスタイルになっていました。 ただ、ネットワーク処理はまたさせることもあるし、失敗することもあるので、 処理待ちをしているとプログラムが固まったみたいになって嬉しくないことも多いんです。

非同期処理については講義で説明したあとにプログラムの紹介をします。