Pythonでurllib3を使ってHTTPリクエストを送信するためのガイド

Web上のリソースは、しばしばURL(Uniform Resource Locator)と呼ばれる、何らかのWebアドレスの下に配置されている(たとえアクセスできない場合でも)。

これらのリソースは、ほとんどの場合、エンドユーザーがHTTPプロトコルを使って、それぞれのHTTPメソッドで操作(取得、更新、削除など)しています。

となります。

このガイドでは、PythonでHTTPリクエストを送信できる urllib3 ライブラリを活用する方法について説明します。

Note: urllib3 モジュールは、Python 3.x でのみ使用することができます。

HTTPとは何ですか?

HTTP (HyperText Transfer Protocol) はデータ転送プロトコルで、通常は HTML などのハイパーメディア文書の転送に使用されますが、JSON、XML などの形式の転送にも使用されることがあります。

OSIモデルのアプリケーション層で、FTP (File Transfer Protocol) や SMTP (Simple Mail Transfer Protocol) などの他のプロトコルと一緒に使用される。

HTTP は今日の World Wide Web のバックボーンであり、HTTP の基本的な通信コンポーネントである HTTP Requests と HTTP Responses のライフサイクルを通じて、Web ブラウザと Web サーバー間の通信チャネルを有効にすることが主な役割です。

HTTP は、クライアントがリソースを要求し、サーバーがリソースまたはその欠如を応答するという、クライアント・サーバーモデルに基づいています。

典型的な HTTP リクエストは以下のようなものです。

GET /tag/java/ HTTP/1.1
Host: stackabuse.com
Accept: */*
User-Agent: Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion


サーバーがリソースを見つけた場合、HTTP レスポンスのヘッダーにはリクエスト/レスポンスサイクルがどのように行われたかというデータが含まれます。

HTTP/1.1 200 OK
Date: Thu, 22 Jul 2021 18:16:38 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
...


そしてレスポンスボディには実際のリソース (この場合、HTML ページ) が含まれます。

<!DOCTYPE html

<html lang="en"
<head
<meta content="Stack Abuse" name="twitter:title"/
<meta content="Learn Python, Java, JavaScript/Node, Machine Learning, and Web Development through articles, code examples, and tutorials for developers of all skill levels." name="twitter:description"/
<meta content="https://stackabuse.com" name="twitter:url"/
<meta content="@StackAbuse" name="twitter:site"/
<meta content="16" name="next-head-count"/
</head
...


urllib3 モジュール

urllib3モジュールは、Python 用に開発された最新の HTTP 関連モジュールで、urllib2` の後継にあたります。

マルチパートエンコーディング、gzip、コネクションプーリング、スレッドセーフを使ったファイルアップロードをサポートしています。

通常、Python 3.xにプリインストールされていますが、そうでない場合は、簡単にインストールすることができます。

$ pip install urllib3


urllib3のバージョンは、モジュールのversion` にアクセスして確認することができます。

import urllib3


# This tutorial is done with urllib3 version 1.25.8
print(urrlib3.__version__)


あるいは、 urllib3 の上に構築された Requests モジュールを使用することもできます。

これはより直感的で人間中心的であり、より広い範囲の HTTP リクエストを可能にします。

もっと詳しく知りたい場合は、PythonのRequestsモジュールへのガイドを読んでください。

HTTPステータスコード

HTTP リクエストが送信されると、リクエストされたリソース (利用可能でアクセス可能な場合) 以外に、HTTP ステータスコードが含まれ、操作がどのように行われたかが示されます。

ステータスコードが何を意味するのか、あるいは少なくともそれが大まかに何を意味するのかを知っておくことは、最も重要なことです。

ということです。

何か問題があるのでしょうか?問題があるのなら、それはリクエストに原因があるのか、サーバーに原因があるのか、それとも私に原因があるのか。

レスポンスコードには、5つのグループがあります。

  1. 情報系コード(100~199)
  2. 成功したコード(200から299まで) – 200が最も一般的なものです。
  3. リダイレクトコード(300から399まで)
  4. クライアントエラーコード(400から499まで)-404が最も一般的です。
  5. サーバーのエラーコード (500 から 599 まで) – 500 が最も一般的です。

urllib3を使用してリクエストを送信するには、PoolManager` クラスのインスタンスを使用します。

これらのリクエストに対するすべてのレスポンスは HTTPResponse インスタンスにまとめられ、当然ながらそのレスポンスの status が含まれます。

import urllib3


http = urllib3.PoolManager()


response = http.request("GET", "http://www.stackabuse.com")
print(response.status) # Prints 200


これらのステータスを使用して、コードのロジックを変更することができます。

もし結果が 200 OK なら、おそらくこれ以上何もする必要はないでしょう。

しかし、もし結果が 405 Method Not Allowed 応答であった場合、あなたのリクエストは恐らく間違って構築されています。

となります。

しかし、ウェブサイトが「418 I’m a teapot」というステータスコードで応答した場合、まれではありますが、それは「ティーポットでコーヒーを淹れることはできない」ということを知らせているのです。

実際には、これは通常、サーバーがリクエストに応答することを望んでおらず、今後も応答しないことを意味します。

もし、特定のリクエストに対して一時的に停止するのであれば、503 Service Unavailableというステータスコードの方がずっと適切です。

注: 418 I'm a teapot というステータスコードは、エイプリルフールのジョークとして追加された、本物ですがおちゃらけたステータスコードです。

プール管理者

コネクションプールとは、将来のリクエストで必要になったときに再利用できる接続のキャッシュで、特定のコマンドを何度も実行するときにパフォーマンスを向上させるために使用されます。

同様に、様々なリクエストを送信するときに、特定の接続を再利用できるように接続プールを作成します。

urllib3ConnectionPoolHTTPConnectionクラスを通して、リクエストとその接続を記録しています。

これらを手作業で作成すると、多くの定型的なコードになってしまうので、ロジックのすべてをPoolManagerに委譲することで、自動的に接続を作成して、プールに追加することができます。

引数num_pools` を調整することで、使用するプールの数を設定することができます。

import urllib3


http = urllib3.PoolManager(num_pools=3)


response1 = http.request("GET", "http://www.stackabuse.com")
response2 = http.request("GET", "http://www.google.com")


HTTP Verb とリクエストを送信するアドレスを渡して request() を送信できるのは、 PoolManager だけです。

異なる動詞は異なる意図を表します。

コンテンツを GET したいのか、サーバに POST したいのか、既存のリソースを PATCH したいのか、あるいは DELETE したいのか、などです。

Python で urllib3 を使って HTTP リクエストを送信する方法

最後に、urllib3 を使って異なるタイプのリクエストを送信する方法と、返されたデータをどのように解釈するかを見てみましょう。

HTTP GETリクエストの送信

HTTP GET リクエストは、クライアントがサーバーからデータを取得する際に使用されます。

Python で HTTP GET リクエストを送信するには、PoolManager インスタンスの request() メソッドを使用し、適切な HTTP Verb とリクエストを送信するリソースを渡します。

import urllib3


http = urllib3.PoolManager()


response = http.request("GET", "http://jsonplaceholder.typicode.com/posts/")


print(response.data.decode("utf-8"))


ここでは、{JSON}に対して GET リクエストを送信しています。

プレースホルダに GET リクエストを送っています。

これは、ダミーの JSON データを生成し、レスポンスのボディで送り返す Web サイトです。

通常、この Web サイトは HTTP リクエストをテストするために使用され、レスポンスをスタブで返します。

HTTPResponseインスタンス、つまり私たちのresponseオブジェクトは、レスポンスのボディを保持しています。

これは、bytesストリームであるdataプロパティでアクセスすることができる。

ウェブサイトは私たちが適さないエンコーディングで応答するかもしれませんし、どのみちbytesstrに変換したいので、データを首尾よくパースできるようにdecode()` して UTF-8 にエンコードしています。

もっと詳しく知りたい方は、「Pythonでバイトを文字列に変換する」についてのガイドをご覧ください。

最後に、レスポンスの本文を表示します。

[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit
suscipit recusandae consequuntur expedita et cum
reprehenderit molestiae ut ut quas totam
nostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae
sequi sint nihil reprehenderit dolor beatae ea dolores neque
fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis
qui aperiam non debitis possimus qui neque nisi nulla"
  },
...


パラメータを指定してHTTP GETリクエストを送信する。

リクエストに特定のパラメータを追加しないことはめったにありません。

パス変数やリクエストパラメータは非常に一般的で、動的なリンク構造やリソースの整理を可能にします。

例えば、ある投稿に対する特定のコメントをAPIで検索したい場合、 http://random.com/posts/get?id=1&amp;commentId=1 とします。

当然ながら、 urllib3 では fields 引数を使用して、GET リクエストにパラメータを追加することができる。

この引数には、パラメータ名とその値の辞書を渡すことができる。

import urllib3


http = urllib3.PoolManager()


response = http.request("GET",
                        "http://jsonplaceholder.typicode.com/posts/", 
                        fields={"id": "1"})


print(response.data.decode("utf-8"))


これは、 id1 である1つのオブジェクトのみを返します。

[
    {
        "userId": 1,
        "id": 1,
        "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
        "body": "quia et suscipit
suscipit recusandae consequuntur expedita et cum
reprehenderit molestiae ut ut quas              totam
nostrum rerum est autem sunt rem eveniet architecto"
    }
]


HTTP POST リクエスト

HTTP POSTリクエストは、クライアント側からサーバー側へデータを送信するために使用されます。

最も一般的な使用方法は、ファイルのアップロードやフォームの入力ですが、ペイロードを使用して任意のデータをサーバーに送信するために使用することができます。

import urllib3


http = urllib3.PoolManager()
response = http.request("POST", "http://jsonplaceholder.typicode.com/posts", fields={"title": "Created Post", "body": "Lorem ipsum", "userId": 5})


print(response.data.decode("utf-8"))


同じ Web アドレスで通信していても、POST リクエストを送信しているので、 fields 引数は取得するデータではなく、サーバに送信するデータを指定することになります。

ここでは、JSON 文字列を送信しています。

これは、titlebodyuserId を持つオブジェクトを表します。

JSON} は Placeholderサービスは、エンティティを追加する機能もスタブしているので、データベースに「追加」できたかどうかを知らせるレスポンスを返し、「作成」された投稿のIDを返します。

{
  "id": 101
}


HTTP DELETE リクエスト

最後に、HTTP DELETE リクエストを送信するには、動詞を "DELETE" に変更し、その id を使って特定の投稿をターゲットにするだけでよいのです。

id1..5` である投稿をすべて削除してみましょう。

import urllib3


http = urllib3.PoolManager()
for i in range(1, 5):
    response = http.request("DELETE", "http://jsonplaceholder.typicode.com/posts", fields={"id": i})
    print(response.data.decode("utf-8"))


リソースが削除されたので、空のボディが返されます。

{}
{}
{}
{}


REST API を作成する場合、リソースの削除が成功したことをユーザーに知らせるために、何らかのステータスコードとメッセージを提供したいと思うことでしょう。

HTTP PATCH リクエストの送信

リソースを更新するために POST リクエストを使用することもできますが、 POST リクエストはリソースを作成するためだけに残しておくのがよい習慣と考えられています。

その代わり、既存のリソースを更新するために PATCH リクエストを発行することができます。

最初の投稿を取得し、新しい titlebody で更新してみましょう。

import urllib3


data = {
    'title': 'Updated title',
    'body': 'Updated body'
}


http = urllib3.PoolManager()


response = http.request("GET", "http://jsonplaceholder.typicode.com/posts/1")
print(response.data.decode('utf-8'))


response = http.request("PATCH", "https://jsonplaceholder.typicode.com/posts/1", fields=data)
print(response.data.decode('utf-8'))


これは次のような結果になるはずです。

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit
suscipit recusandae consequuntur expedita et cum
reprehenderit molestiae ut ut quas totam
nostrum rerum est autem sunt rem eveniet architecto"
}
{
  "userId": 1,
  "id": 1,
  "title": "Updated title",
  "body": "Updated body"
}


Python で urllib3 を使って安全な HTTPS リクエストを送信する

urllib3モジュールは、セキュアな HTTP 接続のためのクライアントサイド SSL 検証も提供します。

これはcertifi` という、標準的な Mozilla 証明書バンドルを提供する別のモジュールの助けを借りて実現できます。

このモジュールのインストールは pip を使って簡単に行うことができます。

$ pip install certifi


certifi.where()で、インストールされている認証局 (CA) を参照します。

これはデジタル証明書を発行するエンティティであり、信頼できます。

これらの信頼できる証明書は、すべてcertifi` モジュールに含まれています。

import urllib3
import certifi


http = urllib3.PoolManager(ca_certs=certifi.where())
response = http.request("GET", "https://httpbin.org/get")


print(response.status)


さて、サーバに安全なリクエストを送ることができます。

urllib3によるファイルのアップロード

urllib3を使うと、ファイルをサーバーにアップロードすることもできる。

ファイルをアップロードするには、データをmultipart/form-dataとしてエンコードし、ファイル名とその内容をfile_name: file_data` というタプルで渡します。

ファイルの内容を読み込むには、Pythonの組み込みメソッドである read() を使用します。

import urllib3
import json


with open("file_name.txt") as f:
    file_data = f.read()


# Sending the request.
resp = urllib3.request(
    "POST",
    "https://reqbin.com/post-online",
    fields= {
       "file": ("file_name.txt", file_data),
    }
)


print(json.loads(resp.data.decode("utf-8"))["files"])


この例では、file_name.txt という名前のファイルを作成し、内容を追加してみましょう。

Some file data
And some more


このスクリプトを実行すると、次のように出力されるはずです。

{'file': 'Some file data
And some more'}


urllib3を使ってファイルを送信すると、レスポンスのdataには“files”属性が含まれます。

この属性には、resp.data.decode(“utf-8”)[“files”]からアクセスできます。

出力をもう少し読みやすくするために、json` モジュールを使用して、レスポンスを読み込み、文字列として表示します。

また、タプルの第3引数として、アップロードされたファイルのMIMEタイプを指定することができます。

... previous code
fields={
  "file": ("file_name.txt", file_data, "text/plain"),
}


結論

このガイドでは、HTTP リクエストとレスポンスを処理するための強力な Python モジュールである urllib3 を使って HTTP リクエストを送信する方法について見てきました。

また、HTTP とは何か、どのようなステータスコードを期待し、それをどのように解釈するか、さらに、certifi を使ってファイルをアップロードし、安全なリクエストを送信する方法についても見てきました。

タイトルとURLをコピーしました