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つのグループがあります。
- 情報系コード(100~199)
- 成功したコード(200から299まで) – 200が最も一般的なものです。
- リダイレクトコード(300から399まで)
- クライアントエラーコード(400から499まで)-404が最も一般的です。
- サーバーのエラーコード (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
というステータスコードは、エイプリルフールのジョークとして追加された、本物ですがおちゃらけたステータスコードです。
プール管理者
コネクションプールとは、将来のリクエストで必要になったときに再利用できる接続のキャッシュで、特定のコマンドを何度も実行するときにパフォーマンスを向上させるために使用されます。
同様に、様々なリクエストを送信するときに、特定の接続を再利用できるように接続プールを作成します。
urllib3は
ConnectionPoolと
HTTPConnectionクラスを通して、リクエストとその接続を記録しています。
これらを手作業で作成すると、多くの定型的なコードになってしまうので、ロジックのすべてを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プロパティでアクセスすることができる。
ウェブサイトは私たちが適さないエンコーディングで応答するかもしれませんし、どのみちbytesを
strに変換したいので、データを首尾よくパースできるように
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&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"))
これは、 id
が 1
である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 文字列を送信しています。
これは、title
、body
、userId
を持つオブジェクトを表します。
JSON} は Placeholderサービスは、エンティティを追加する機能もスタブしているので、データベースに「追加」できたかどうかを知らせるレスポンスを返し、「作成」された投稿のIDを返します。
{
"id": 101
}
HTTP DELETE リクエスト
最後に、HTTP DELETE リクエストを送信するには、動詞を "DELETE"
に変更し、その id
を使って特定の投稿をターゲットにするだけでよいのです。
idが
1..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
リクエストを発行することができます。
最初の投稿を取得し、新しい title
と body
で更新してみましょう。
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
を使ってファイルをアップロードし、安全なリクエストを送信する方法についても見てきました。