Python 3でWeb APIを使用する方法

前書き

API、またはApplicationProgramInterfaceを使用すると、開発者は1つのアプリを別のアプリと統合できます。 プログラムの内部動作の一部を限定的な方法で公開します。

APIを使用して、他のプログラムから情報を取得したり、Webブラウザーで通常行うことを自動化することができます。 APIを使用して、他の方法ではできないことを実行できる場合があります。 驚くべき数のWebプロパティが、Twitter、Facebook、GitHub、DigitalOceanなど、より馴染みのあるWebサイトまたはモバイルアプリとともにWebベースのAPIを提供しています。

how to code in Python 3に関するいくつかのチュートリアルを完了し、Pythonの構文、構造、およびいくつかの組み込みfunctionsに慣れている場合は、お気に入りを利用するPythonプログラムを作成できます。 API。

このガイドでは、PythonをDigitalOcean APIで使用して、DigitalOceanアカウントに関する情報を取得する方法を学習します。 次に、学習した内容をGitHub’s APIに適用する方法を見ていきます。

完了したら、Web APIに共通の概念を理解し、他のサービスからAPIを試すために使用できるステップバイステップのプロセスと実際のコードサンプルが得られます。

前提条件

このガイドを始める前に、次のものが必要です。

[[step-1 -—- getting-familiar-with-an-api]] ==ステップ1—APIに精通する

新しいAPIを使用するための最初のステップは、ドキュメントを見つけて、ベアリングを取得することです。 DigitalOcean APIのドキュメントはhttps://developers.digitalocean.com/から始まります。 他のサービスのAPIを見つけるには、サイトの名前と「API」を検索します。すべてのサービスがフロントページでそれらを宣伝するわけではありません。

一部のサービスにはAPI wrappersがあります。 APIラッパーは、選択したプログラミング言語でAPIを使いやすくするためにシステムにインストールするコードです。 このガイドでは、APIの内部動作の多くを隠し、多くの場合APIが実行できるすべてを公開しないため、ラッパーを使用しません。 ラッパーは、何かを迅速に完了させたい場合に最適ですが、API自体ができることをしっかりと理解することは、ラッパーが目的に合っているかどうかを判断するのに役立ちます。

まず、https://developers.digitalocean.com/documentation/v2/にあるDigitalOcean APIの概要を見て、要求の送信方法と応答で何を期待するかについての基本のみを理解するようにしてください。 この時点では、次の3つだけを学習しようとしています。

  1. リクエストはどのようなものですか? それらはすべてURLだけですか? より詳細なリクエストの場合、データはどのようにフォーマットされますか? 通常、これはWebブラウザが使用するようなJSONまたはクエリ文字列パラメータですが、XMLまたはカスタム形式を使用するものもあります。

  2. 応答はどのように見えますか? APIドキュメントには、リクエストとレスポンスのサンプルが表示されます。 JSON、XML、またはその他の種類の応答を取得しますか?

  3. 要求または応答ヘッダーには何が入りますか? 多くの場合、要求ヘッダーには認証トークンが含まれ、応答ヘッダーには、レート制限にどれだけ近いかなど、サービスの使用に関する現在の情報が提供されます。

DigitalOcean APIは、HTTPmethodsverbsと呼ばれることもあります)を使用して、既存の情報を読み取ろうとしているのか、新しい情報を作成しようとしているのか、何かを削除しようとしているのかを示します。 ドキュメントのこの部分では、使用されるメソッドと目的を説明します。 一般に、GETリクエストはPOSTよりも簡単ですが、ここで作業を終える頃には、それほど大きな違いはありません。

APIドキュメントの次のセクションでは、サーバーがリクエストにどのように応答するかについて説明します。 一般に、リクエストは成功するか失敗します。 失敗した場合、原因はリクエストに問題があるか、サーバー上の問題です。 この情報はすべて、カテゴリに分類された3桁の数字であるHTTP status codesを使用して伝達されます。

  • 200シリーズは「成功」を意味します—要求は有効であり、応答は論理的にそれに続くものです。

  • 400シリーズは、「不正な要求」を意味します—要求に問題があったため、サーバーは要求どおりに処理しませんでした。 HTTP400レベルのエラーの一般的な原因は、フォーマットが不適切な要求と認証の問題です。

  • 500シリーズは、「サーバーエラー」を意味します。リクエストは問題ない可能性がありますが、サーバーは、制御できない理由により、現在、適切な応答を提供できませんでした。 これらはまれなはずですが、コードでそれらを処理できるように、可能性を認識する必要があります。

コードは、HTTPステータスコードを使用して何かを実行する前に、常にHTTPステータスコードをチェックする必要があります。 これを行わないと、不完全な情報でトラブルシューティングに時間を浪費することになります。

リクエストを送信する方法と、レスポンスで何を検索するかについての一般的な考えができたので、最初のリクエストを送信します。

[[step-2 -—- getting-information-from-the-web-api]] ==ステップ2— WebAPIから情報を取得する

DigitalOceanアカウントには、Web UIには表示されていない管理情報が含まれています。 APIは、馴染みのある情報の異なるビューを提供できます。 この別のビューを見るだけで、APIで何をしたいのかというアイデアが浮かんだり、知らなかったサービスやオプションが明らかになったりすることがあります。

スクリプト用のプロジェクトを作成することから始めましょう。 apisというプロジェクトの新しいディレクトリを作成します。

mkdir apis

次に、この新しいディレクトリに移動します。

cd apis

このプロジェクトの新しいvirtualenvを作成します。

python3 -m venv apis

virtualenvを起動します。

source apis/bin/activate

次に、requestsライブラリをインストールします。これをスクリプトで使用して、スクリプトでHTTPリクエストを作成します。

pip install requests

環境を構成した状態で、do_get_account.pyという名前の新しいPythonファイルを作成し、テキストエディターで開きます。 JSONおよびHTTPリクエストを処理するために、このプログラムをimporting librariesで開始します。

do_get_account.py

import json
import requests

これらのimportステートメントは、JSONデータ形式とHTTPプロトコルを操作できるようにするPythonコードをロードします。 これらのライブラリを使用しているのは、HTTPリクエストを送信する方法や、有効なJSONを解析および作成する方法の詳細に関心がないためです。これらを使用してこれらのタスクを実行したいだけです。 このチュートリアルのスクリプトはすべて、このように始まります。

次に、すべてのリクエストで同じ情報を保持するために、いくつかのvariablesを設定します。 これにより、何度も何度も入力する必要がなくなり、何か変更があった場合に更新を行うための単一の場所が得られます。 importステートメントの後に、これらの行をファイルに追加します。

do_get_account.py

...
api_token = 'your_api_token'
api_url_base = 'https://api.digitalocean.com/v2/'

api_token変数は、DigitalOceanAPIトークンを保持する文字列です。 例の値を独自のトークンに置き換えます。 api_url_base変数は、DigitalOceanAPIのすべてのURLで始まる文字列です。 コードの後半で必要に応じて追加します。

次に、APIドキュメントで説明されている方法でHTTPリクエストヘッダーを設定する必要があります。 これらの行をファイルに追加して、リクエストヘッダーを含むdictionaryを設定します。

do_get_account.py

...
headers = {'Content-Type': 'application/json',
           'Authorization': 'Bearer {0}'.format(api_token)}

これにより、2つのヘッダーが同時に設定されます。 Content-Typeヘッダーは、リクエストの本文にJSON形式のデータを期待するようにサーバーに指示します。 Authorizationヘッダーにはトークンを含める必要があるため、Pythonの文字列フォーマットロジックを使用して、文字列を作成するときにapi_token変数を文字列に挿入します。 トークンをリテラル文字列としてここに入れることもできますが、これを分離することで、今後いくつかのことが容易になります。

  • トークンを置き換える必要がある場合は、別の変数を使用すると、どこでそれを行うかを簡単に確認できます。

  • コードを誰かと共有したい場合は、APIトークンを削除するのが簡単になり、友人がコードを置く場所を簡単に確認できます。

  • 自己文書化されています。 APIトークンが文字列リテラルとしてのみ使用される場合、コードを読んでいる人は自分が見ているものを理解できない可能性があります。

これらのセットアップの詳細が網羅されたので、実際にリクエストを送信します。 リクエストの作成と送信を開始したいかもしれませんが、もっと良い方法があります。 このロジックをリクエストの送信とレスポンスの読み取りを処理する関数に配置する場合、何をしているのかをもう少し明確に考える必要があります。 また、テストと再利用をより簡単にするコードになります。 それが私たちがやろうとしていることです。

この関数は、作成した変数を使用してリクエストを送信し、Python辞書でアカウント情報を返します。

この初期段階でロジックを明確にするため、詳細なエラー処理はまだ行いませんが、すぐに追加します。

アカウント情報を取得する関数を定義します。 関数の機能にちなんで名前を付けることをお勧めします。これはアカウント情報を取得するため、get_account_infoと呼びます。

do_get_account.py

...
def get_account_info():

    api_url = '{0}account'.format(api_url_base)

    response = requests.get(api_url, headers=headers)

    if response.status_code == 200:
        return json.loads(response.content.decode('utf-8'))
    else:
        return None

ヘッダーで使用したのと同様のPythonの文字列フォーマット方法を使用して、api_urlの値を作成します。文字列accountの前にAPIのベースURLを追加して、アカウント情報を返すURLであるURLhttps://api.digitalocean.com/v2/accountを取得します。

response変数は、Pythonrequestsmoduleによって作成されたオブジェクトを保持します。 この行は、スクリプトの開始時に定義したヘッダーを使用して作成したURLに要求を送信し、APIから応答を返します。

次に、レスポンスのHTTPステータスコードを確認します。

成功した応答である200の場合、jsonモジュールのloads関数を使用して文字列をJSONとしてロードします。 ロードする文字列は、responseオブジェクトresponse.contentのコンテンツです。 .decode('utf-8')の部分は、DigitalOcean APIからのすべての応答がそうであるように、このコンテンツがUTF-8文字セットを使用してエンコードされていることをPythonに通知します。 jsonモジュールは、そのオブジェクトからオブジェクトを作成します。これをこの関数の戻り値として使用します。

応答がnot200の場合、Noneを返します。これは、この関数を呼び出すときに確認できるPythonの特別な値です。 この時点でエラーを無視していることに気付くでしょう。 これは、「成功」ロジックを明確に保つためです。 より包括的なエラーチェックを近日中に追加します。

次に、この関数を呼び出して、適切な応答が得られたことを確認し、APIが返した詳細を出力します。

do_get_account.py

...
account_info = get_account_info()

if account_info is not None:
    print("Here's your info: ")
    for k, v in account_info['account'].items():
        print('{0}:{1}'.format(k, v))

else:
    print('[!] Request Failed')

account_info = get_account_info()は、account_info変数をget_account_info()の呼び出しから戻ってきたものに設定するため、特別な値Noneになるか、またはに関する情報のコレクションになります。アカウント。

Noneでない場合は、すべてのPython辞書にあるitems()メソッドを使用して、各情報を独自の行に出力します。

それ以外の場合(つまり、account_infoNoneの場合)、エラーメッセージが出力されます。

ここでちょっと休みましょう。 二重否定を含むこのifステートメントは、最初は扱いにくいと感じるかもしれませんが、これは一般的なPythonイディオムです。 その長所は、エラーケースを処理した後ではなく、成功時に実行されるコードをconditionalに非常に近く保つことです。

必要に応じて別の方法で行うこともできますが、実際にそのコードを自分で記述するのは良い練習になるかもしれません。 if account_info is not None:の代わりに、if account_info is None:から始めて、残りがどのように配置されるかを確認できます。

スクリプトを保存して試してください:

python do_get_account.py

出力は次のようになります。

OutputHere's your info:
droplet_limit:25
email:[email protected]
status:active
floating_ip_limit:3
email_verified:True
uuid:123e4567e89b12d3a456426655440000
status_message:

これで、APIからデータを取得する方法がわかりました。 次に、APIを使用してデータを変更する、もう少し興味深いものに進みます。

[[step-3 -—- modifying-information-on-the-server]] ==ステップ3—サーバー上の情報の変更

読み取り専用リクエストで練習したら、変更を開始します。 PythonとDigitalOcean APIを使用して、SSHキーをDigitalOceanアカウントに追加して、これを調べてみましょう。

まず、https://developers.digitalocean.com/documentation/v2/#ssh-keysで入手できるSSHキーのAPIドキュメントを見てください。

APIを使用すると、アカウントの現在のSSHキーを一覧表示したり、新しいキーを追加したりできます。 SSHキーのリストを取得するリクエストは、アカウント情報を取得するリクエストとよく似ています。 ただし、応答は異なります。アカウントとは異なり、0、1、または多くのSSHキーを持つことができます。

このスクリプト用にdo_ssh_keys.pyという名前の新しいファイルを作成し、最後のファイルとまったく同じように開始します。 jsonおよびrequestsモジュールをインポートして、JSONまたはHTTPプロトコルの詳細について心配する必要がないようにします。 次に、DigitalOcean APIトークンを変数として追加し、辞書にリクエストヘッダーを設定します。

do_ssh_keys.py

import json
import requests


api_token = 'your_api_token'
api_url_base = 'https://api.digitalocean.com/v2/'
headers = {'Content-Type': 'application/json',
           'Authorization': 'Bearer {0}'.format(api_token)}

SSHキーを取得するために作成する関数は、アカウント情報を取得するために使用した関数と似ていますが、今回はエラーをより直接処理します。

まず、API呼び出しを行い、応答をresponse応答変数に格納します。 ただし、api_urlは前のスクリプトと同じではありません。今回はhttps://api.digitalocean.com/v2/account/keysを指す必要があります。

このコードをスクリプトに追加します。

do_ssh_keys.py

...
def get_ssh_keys():

    api_url = '{0}account/keys'.format(api_url_base)

    response = requests.get(api_url, headers=headers)

次に、応答のHTTPステータスコードを見て、エラー処理を追加します。 200の場合、以前と同じように、応答の内容を辞書として返します。 それ以外の場合は、ステータスコードの種類に関連する有用なエラーメッセージを出力してから、Noneを返します。

次の行をget_ssh_keys関数に追加します。

do_ssh_keys.py

...

    if response.status_code >= 500:
        print('[!] [{0}] Server Error'.format(response.status_code))
        return None
    elif response.status_code == 404:
        print('[!] [{0}] URL not found: [{1}]'.format(response.status_code,api_url))
        return None
    elif response.status_code == 401:
        print('[!] [{0}] Authentication Failed'.format(response.status_code))
        return None
    elif response.status_code == 400:
        print('[!] [{0}] Bad Request'.format(response.status_code))
        return None
    elif response.status_code >= 300:
        print('[!] [{0}] Unexpected Redirect'.format(response.status_code))
        return None
    elif response.status_code == 200:
        ssh_keys = json.loads(response.content.decode('utf-8'))
        return ssh_keys
    else:
        print('[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format(response.status_code, response.content))
    return None

このコードは、応答内のHTTPステータスコードを調べることにより、6つの異なるエラー状態を処理します。

  • 500以上のコードは、サーバーに問題があることを示します。 これらはまれであり、リクエストの問題が原因ではないため、ステータスコードのみを出力します。

  • 404のコードは「見つかりません」を意味します。これは、おそらくURLのタイプミスに起因します。 このエラーについては、ステータスコードとその原因となったURLを出力するため、失敗の理由を確認できます。

  • 401のコードは、認証が失敗したことを意味します。 これの最も可能性の高い原因は、api_keyが正しくないか欠落していることです。

  • 300の範囲のコードは、リダイレクトを示します。 DigitalOcean APIはリダイレクトを使用しないため、これは決して発生しませんが、エラーを処理している間は確認するのに問題はありません。 多くのバグは、プログラマーが決して発生してはならないと考えたことによって引き起こされます。

  • 200のコードは、要求が正常に処理されたことを意味します。 このため、何も印刷しません。 前のスクリプトで使用したのと同じ構文を使用して、sshキーをJSONオブジェクトとして返すだけです。

  • 応答コードがそれ以外の場合、ステータスコードを「予期しないエラー」として出力します。

これで、APIの呼び出しで発生する可能性のあるエラーを処理できます。 この時点で、エラーメッセージとNoneオブジェクトがあるか、成功して0個以上のSSHキーを含むJSONオブジェクトがあります。 次のステップは、それらを印刷することです。

do_ssh_keys.py

...

ssh_keys = get_ssh_keys()

if ssh_keys is not None:
    print('Here are your keys: ')
    for key, details in enumerate(ssh_keys['ssh_keys']):
        print('Key {}:'.format(key))
        for k, v in details.items():
            print('  {0}:{1}'.format(k, v))
else:
    print('[!] Request Failed')

応答にはSSHキーのlist(または配列)が含まれているため、すべてのキーを表示するためにリスト全体を反復処理する必要があります。 これにはPythonのenumerateメソッドを使用します。 これは、辞書で使用できるitemsメソッドに似ていますが、代わりにリストで機能します。

for loopだけでなく、enumerateを使用します。これは、特定のキーについて、リストのどこまで進んでいるかを確認できるようにするためです。

各キーの情報はディクショナリとして返されるため、前のスクリプトのアカウント情報ディクショナリで使用したのと同じfor k,v in details.items():コードを使用します。

このスクリプトを実行すると、アカウントに既に存在するSSHキーのリストが表示されます。

python get_ssh_keys.py

アカウントにすでにあるSSHキーの数に応じて、出力は次のようになります。

OutputHere are your keys:
Kcy 0:
  id:280518
  name:work
  fingerprint:96:f7:fb:9f:60:9c:9b:f9:a9:95:01:5c:5c:2c:d5:a0
  public_key:ssh-rsa AAAAB5NzaC1yc2cAAAADAQABAAABAQCwgr9Fzc/YTD/V2Ka5I52Rx4I+V2Ka5I52Rx4Ir5LKSCqkQ1Cub+... sammy@work
Kcy 1:
  id:290536
  name:home
  fingerprint:90:1c:0b:ac:fa:b0:25:7c:af:ab:c5:94:a5:91:72:54
  public_key:ssh-rsa AAAAB5NzaC1yc2cAAAABJQAAAQcAtTZPZmV96P9ziwyr5LKSCqkQ1CubarKfK5r7iNx0RNnlJcqRUqWqSt... sammy@home

アカウントのSSHキーをリストできるようになったので、ここでの最後のスクリプトは、リストに新しいキーを追加するスクリプトになります。

新しいSSHキーを追加する前に、キーを生成する必要があります。 このステップのより完全な取り扱いについては、チュートリアルHow to Set Up SSH Keysをご覧ください。

ただし、この目的のためには、単純なキーが必要です。 このコマンドを実行して、Linux、BSD、またはMacOSで新しいコマンドを生成します。 必要に応じて、既存のドロップレットでこれを実行できます。

ssh-keygen -t rsa

プロンプトが表示されたら、ファイルを入力してキーを保存し、パスフレーズを提供しません。

OutputGenerating public/private rsa key pair.
Enter file in which to save the key (/home/sammy/.ssh/id_rsa): /home/sammy/.ssh/sammy
Created directory '/home/sammy/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/sammy/.ssh/sammy.
Your public key has been saved in /home/sammy/.ssh/sammy.pub.
...

公開鍵ファイルはスクリプトに必要になるため、保存場所をメモしてください。

新しいPythonスクリプトを開始し、それをadd_ssh_key.pyと呼び、他のスクリプトと同じように開始します。

add_ssh_key.py

import json
import requests


api_token = 'your_api_token'
api_url_base = 'https://api.digitalocean.com/v2/'
headers = {'Content-Type': 'application/json',
           'Authorization': 'Bearer {0}'.format(api_token)}

関数を使用してリクエストを行いますが、これは少し異なります。

2つの引数を受け入れるadd_ssh_keyという関数を作成します。新しいSSHキーに使用する名前と、ローカルシステム上のキー自体のファイル名です。 この関数はread the fileを実行し、GETの代わりにHTTPPOST要求を行います。

add_ssh_key.py

...

def add_ssh_key(name, filename):

    api_url = '{0}account/keys'.format(api_url_base)

    with open(filename, 'r') as f:
        ssh_key = f.readline()

    ssh_key = {'name': name, 'public_key': ssh_key}

    response = requests.post(api_url, headers=headers, json=ssh_key)

with open(filename, 'r') as f:はファイルを読み取り専用モードで開き、次の行はファイルから最初の(そして唯一の)行を読み取り、それをssh_key変数に格納します。

次に、APIが期待する名前と値を使用してssh_keyというPython辞書を作成します。

ただし、リクエストを送信すると、もう少し新しいものがあります。 これはGETではなくPOSTであり、JSONとしてエンコードされたPOSTリクエストの本文でssh_keyを送信する必要があります。 requestsモジュールが詳細を処理します。 requests.postは、POSTメソッドを使用するように指示し、json=ssh_keyを含めると、JSONとしてエンコードされたssh_key変数をリクエストの本文に送信するように指示します。

APIによると、応答は成功すると200ではなくHTTP201になり、応答の本文には追加したばかりのキーの詳細が含まれます。

次のエラー処理コードをadd_ssh_key関数に追加します。 前のスクリプトと似ていますが、今回は成功するために200ではなくコード201を探す必要があります。

add_ssh_key.py

...
    if response.status_code >= 500:
        print('[!] [{0}] Server Error'.format(response.status_code))
        return None
    elif response.status_code == 404:
        print('[!] [{0}] URL not found: [{1}]'.format(response.status_code,api_url))
        return None
    elif response.status_code == 401:
        print('[!] [{0}] Authentication Failed'.format(response.status_code))
        return None
    elif response.status_code >= 400:
        print('[!] [{0}] Bad Request'.format(response.status_code))
        print(ssh_key )
        print(response.content )
        return None
    elif response.status_code >= 300:
        print('[!] [{0}] Unexpected redirect.'.format(response.status_code))
        return None
    elif response.status_code == 201:
        added_key = json.loads(response.content)
        return added_key
    else:
        print('[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format(response.status_code, response.content))
        return None

この関数は、前の関数と同様に、Noneまたは応答コンテンツのいずれかを返すため、前と同じアプローチを使用して結果を確認します。

次に、関数を呼び出して結果を処理します。 2番目の引数として、新しく作成したSSHキーへのパスを渡します。

add_ssh_key.py

...
add_response = add_ssh_key('tutorial_key', '/home/sammy/.ssh/sammy.pub')

if add_response is not None:
    print('Your key was added: ' )
    for k, v in add_response.items():
        print('  {0}:{1}'.format(k, v))
else:
    print('[!] Request Failed')

このスクリプトを実行すると、新しいキーが追加されたことを知らせる応答が返されます。

python add_ssh_key.py

出力は次のようになります。

OutputYour key was added:
  ssh_key:{'id': 9458326, 'name': 'tutorial_key', 'fingerprint': '64:76:37:77:c8:c7:26:05:f5:7b:6b:e1:bb:d6:80:da', 'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCUtY9aizEcVJ65/O5CE6tY8Xodrkkdh9BB0GwEUE7eDKtTh4NAxVjXc8XdzCLKtdMwfSg9xwxSi3axsVWYWBUhiws0YRxxMNTHCBDsLFTJgCFC0JCmSLB5ZEnKl+Wijbqnu2r8k2NoXW5GUxNVwhYztXZkkzEMNT78TgWBjPu2Tp1qKREqLuwOsMIKt4bqozL/1tu6oociNMdLOGUqXNrXCsOIvTylt6ROF3a5UnVPXhgz0qGbQrSHvCEfuKGZ1kw8PtWgeIe7VIHbS2zTuSDCmyj1Nw1yOTHSAqZLpm6gnDo0Lo9OEA7BSFr9W/VURmTVsfE1CNGSb6c6SPx0NpoN sammy@tutorial-test'}

「成功」条件を変更して200ではなくHTTP201を検索するのを忘れた場合、エラーが報告されますが、キーはまだ追加されています。 エラー処理により、ステータスコードは201であることがわかります。 成功を示す200シリーズのメンバーとしてそれを認識する必要があります。 これは、基本的なエラー処理がトラブルシューティングを簡単にする方法の例です。

このスクリプトでキーを正常に追加したら、もう一度実行して、既に存在するキーを追加しようとしたときに何が起こるかを確認します。

APIはHTTP422応答を送り返します。これは、スクリプトが「SSHキーは既にアカウントで使用されています」というメッセージに変換されます。

Output[!] [422] Bad Request
{'name': 'tutorial_key', 'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCUtY9aizEcVJ65/O5CE6tY8Xodrkkdh9BB0GwEUE7eDKtTh4NAxVjXc8XdzCLKtdMwfSg9xwxSi3axsVWYWBUhiws0YRxxMNTHCBDsLFTJgCFC0JCmSLB5ZEnKl+Wijbqnu2r8k2NoXW5GUxNVwhYztXZkkzEMNT78TgWBjPu2Tp1qKREqLuwOsMIKt4bqozL/1tu6oociNMdLOGUqXNrXCsOIvTylt6ROF3a5UnVPXhgz0qGbQrSHvCEfuKGZ1kw8PtWgeIe7VIHbS2zTuSDCmyj1Nw1yOTHSAqZLpm6gnDo0Lo9OEA7BSFr9W/VURmTVsfE1CNGSb6c6SPx0NpoN sammy@tutorial-test'}
b'{"id":"unprocessable_entity","message":"SSH Key is already in use on your account"}'
[!] Request Failed

ここで、get_ssh_keys.pyスクリプトを再度実行すると、新しく追加されたキーがリストに表示されます。

わずかな変更を加えるだけで、これら2つのスクリプトを使用して、必要なときにいつでも新しいSSHキーをDigitalOceanアカウントに追加できます。 このAPIの関連関数を使用すると、一意のキーIDまたはフィンガープリントを使用して、特定のキーの名前を変更または削除できます。

別のAPIを見て、今学んだスキルがどのように翻訳されるかを見てみましょう。

[[step-4 -—- working-with-a-different-api]] ==ステップ4—別のAPIでの作業

GitHubにはAPIもあります。 DigitalOcean APIの使用について学んだことはすべて、GitHub APIの使用に直接適用できます。

DigitalOceanで行ったのと同じ方法でGitHub APIを理解してください。 API documentationを検索し、Overviewセクションをスキミングします。 GitHub APIとDigitalOcean APIにはいくつかの類似点があることがすぐにわかります。

まず、すべてのAPI URLに共通のルートhttps://api.github.com/があることに気付くでしょう。 これをコード内の変数として使用して、エラーの可能性を合理化し、減らす方法を知っています。

GitHubのAPIは、DigitalOceanと同じようにJSONをリクエストおよびレスポンスの形式として使用しているため、これらのリクエストの作成方法とレスポンスの処理方法を知っています。

応答には、DigitalOceanとほぼ同じ名前とまったく同じ値を使用して、HTTP応答ヘッダーにレート制限に関する情報が含まれます。

GitHubは認証にOAuthを使用し、リクエストヘッダーでトークンを送信できます。 このトークンの詳細は少し異なりますが、使用方法はDigitalOceanのAPIで行った方法と同じです。

いくつかの違いもあります。 GitHubでは、使用するAPIのバージョンを示すリクエストヘッダーの使用を推奨しています。 Pythonでリクエストにヘッダーを追加する方法を知っています。

また、GitHubでは、リクエストで一意のUser-Agent文字列を使用する必要があるため、コードが問題を引き起こしている場合に、より簡単にユーザーを見つけることができます。 これもヘッダーで処理します。

GitHub APIは同じHTTPリクエストメソッドを使用しますが、特定の操作にはPATCHという新しいメソッドも使用します。 GitHub APIは、GETを使用して情報を読み取り、POSTを使用して新しいアイテムを追加し、PATCHを使用して既存のアイテムを変更します。 このPATCHリクエストは、APIドキュメントで注目したい種類のものです。

すべてのGitHub API呼び出しが認証を必要とするわけではありません。 たとえば、アクセストークンを必要とせずに、ユーザーのリポジトリのリストを取得できます。 そのリクエストを行い、結果を表示するスクリプトを作成しましょう。

このスクリプトのエラー処理を簡素化し、1つのステートメントのみを使用して、考えられるすべてのエラーを処理します。 さまざまな種類のエラーを個別に処理するためにコードが常に必要なわけではありませんが、エラー状態で何かをするのは良いことです。

エディタでgithub_list_repos.pyという名前の新しいファイルを作成し、次のコンテンツを追加します。これはかなり見覚えがあるはずです。

github_list_repos.py

import json
import requests


api_url_base = 'https://api.github.com/'
headers = {'Content-Type': 'application/json',
           'User-Agent': 'Python Student',
           'Accept': 'application/vnd.github.v3+json'}

インポートは、これまで使用してきたものと同じです。 api_url_baseは、すべてのGitHubAPIが始まる場所です。

ヘッダーには、GitHubが概要で言及しているオプションの2つに加えて、リクエストでJSON形式のデータを送信しているというヘッダーが含まれています。

これは小さなスクリプトですが、ロジックをモジュール化してリクエストを行うためのロジックをカプセル化するために、関数を定義します。 多くの場合、小さなスクリプトはより大きなスクリプトに成長するため、これについて熱心に取り組むと役に立ちます。 ユーザー名を引数として受け入れるget_reposという関数を追加します。

github_list_repos.py

...
def get_repos(username):

    api_url = '{}orgs/{}/repos'.format(api_url_base, username)

    response = requests.get(api_url, headers=headers)

    if response.status_code == 200:
        return (response.content)
    else:
        print('[!] HTTP {0} calling [{1}]'.format(response.status_code, api_url))
        return None

関数内では、api_url_base、関心のあるユーザーの名前、およびGitHubにリポジトリリストが必要であることを通知するURLの静的部分からURLを作成します。 次に、応答のHTTPステータスコードをチェックして、200(成功)であることを確認します。 成功した場合、応答コンテンツを返します。 そうでない場合は、実際のステータスコードと作成したURLを出力します。これにより、どこが間違っているのかがわかります。

次に、関数を呼び出して、使用するGitHubユーザー名を渡します。 この例ではoctokitを使用します。 次に、結果を画面に印刷します。

github_list_repos.py

...
repo_list = get_repos('octokit')

if repo_list is not None:
    print(repo_list)
else:
    print('No Repo List Found')

ファイルを保存し、スクリプトを実行して、指定したユーザーのリポジトリを表示します。

python github_list_repos.py

この例では応答をJSONとして解析しておらず、結果を特定のキーにフィルター処理していないため、出力には多くのデータが表示されます。 他のスクリプトで学んだことを使用してそれを行います。 取得した結果を見て、リポジトリ名を印刷できるかどうかを確認します。

これらのGitHub APIの便利な点は、認証が不要なリクエストにWebブラウザーで直接アクセスできることです。これにより、スクリプトで表示されるものとレスポンスを比較できます。 ブラウザでhttps://api.github.com/orgs/octokit/reposにアクセスして、そこでの応答を確認してください。

これで、GitHub APIを使用して独自の目標をサポートするために、ドキュメントを読み、より具体的なリクエストを送信するために必要なコードを記述する方法を理解できました。

このチュートリアルのすべての例の完成したコードは、this repository on GitHubにあります。

結論

このチュートリアルでは、スタイルがわずかに異なる2つの異なるサービスでWeb APIを使用する方法を学びました。 デバッグを容易にし、スクリプトをより堅牢にするために、エラー処理コードを含めることの重要性を見てきました。 Pythonモジュールrequestsjsonを使用して、これらのテクノロジーの詳細からユーザーを隔離し、作業を完了し、要求と応答の処理を関数にカプセル化して、スクリプトをよりモジュール化しました。

さらに、新しいWeb APIを学習する際に従うべき反復可能なプロセスがあります。

  1. APIを操作する方法の基礎を理解するには、ドキュメントを見つけて概要を読んでください。

  2. 必要に応じて認証トークンを取得し、基本的なエラー処理を備えたモジュラースクリプトを記述して、単純なリクエストを送信し、エラーに応答し、応答を処理します。

  3. サービスから必要な情報を取得するリクエストを作成します。

ここで、この新たに得られた知識を固め、使用する別のAPI、またはここで使用したAPIの別の機能を見つけます。 独自のプロジェクトは、ここで学んだことを固めるのに役立ちます。

Related