【Requests】レスポンスヘッダーだけを取得するコード例【Python】

Python の Requests で、『HTTP レスポンスヘッダー』だけを取得するコード例です。

レスポンスヘッダーの内容を見てから、本文 (body) を『受信するか?しないか?』を決めるために書きました。

レスポンスヘッダーだけを取得する方法は、以下の 2 種類を試しました。

  • GET メソッドの requests.get(url, stream=True) を使用した方法。
  • HEAD メソッドの requests.head(url, **kwargs) を使用した方法。

『GET メソッド』の方は、レスポンスヘッダーの内容によって、本文を『受信するか?しないか?』を分ける場合に良さそうでした。

一方で、『HEAD メソッド』の方は、ウェブページの状態を確認するだけ、という場合に良さそうでした。

ところで、レスポンスヘッダーの内容は、GET メソッドで取得した場合と、HEAD メソッドで取得した場合とで、異なる場合がありました。

当然のことながら、どのメソッドで、どんなレスポンスヘッダーが返ってくるかは、ウェブサイトのサーバーによって異なっていました。

マニュアルの場所

レスポンスヘッダー』の説明です。

(MDN Web Docs) Response header (レスポンスヘッダー)

コード例で使用した Requests と Python のマニュアルの場所です。

(Requests) requests.request(method, url, **kwargs)

(Requests) requests.get(url, params=None, **kwargs)

(Requests) requests.head(url, **kwargs)

(Requests) Timeouts – requests.get(url, timeout)

(Requests) class requests.Response

(Requests) requests.Response.close()

.close() は、通常は明示的に呼び出す必要は無いとのことでした。

ですが、.get(url, stream=True) で、かつ、本文を受信しなかったときは、with 文などで閉じる必要がありました。

(Requests) Body Content Workflow – with requests.get(url, stream=True) as r:

(Requests) requests.Response.headers=None

(Requests) requests.Response.content

(Requests) requests.Response.raw=None

(Python) HTTPResponse.closed

(Python) dict – get(key[, default]) KeyError を避けるために使用しました。

Response.headers の特別な辞書にも、これと同じ働きをする .get() がありました。

GET メソッドのコード例

GET メソッドで、最初に『レスポンスヘッダー』だけを取得するコード例です。

本文は、'Content-Type''text/html' があるときだけ、取得するようにしてみました。

"""get_method.py"""
import requests

def main():
    """メイン関数です。"""
    print('start\n')

    print('■ URL を決めます。')
    url = 'https://example.com/'
    print(f'{url}\n')

    print('■ GET メソッド (stream=True) で URL を開きます。\n')
    # stream=True を指定したときは、with 文を使用して、
    # 最終的に接続が閉じるようにします。
    # たとえば、レスポンスヘッダーの内容を見て、結局
    # r.content 属性にアクセスしなかった場合です。その場合は、
    # 接続が開いたままになっていました。なので、
    # それを with 文の働きで閉じてあげます。
    with requests.get(url, stream=True, timeout=30) as r:
        print('■ レスポンスヘッダーの内容を表示します。')
        print(f'type(r) {type(r)}')
        print(f'type(r.headers) {type(r.headers)}')
        for (k, v) in r.headers.items():
            print(f'  {k}: {v}')
        print('(ヘッダの表示おわり)\n')

        print('■ 接続が『まだ閉じていない』ことを確認します。')
        # r.raw.closed 属性が False になっているはずです。
        print(f'r.raw.closed: {r.raw.closed} (← まだ False のはず)\n')

        print('■ まだ、『本文を受信していない』ことを確認します。')
        # 隠し属性の r._content が、False になっているはずです。
        print(f'type(r._content)): {type(r._content)}')
        print(f'r._content: {r._content} (← まだ False のはず)\n')

        # たとえば、レスポンスヘッダを見て、
        # 'Content-Type' に 'text/html' があるときだけ、
        # 本文を受信するようにしてみます。
        if 'text/html' in r.headers.get('Content-Type', default=''):
            # (説明) r.headers['Content-Type'] だと、キーが無かった時に、
            # KeyError が発生しました。なので .get(key, default='') で、
            # キーが無かった時に『空文字列』を返すようにしました。

            print('(r.content 属性にアクセスします)\n')
            # まだ本文を受信していなければ、
            # この属性にアクセスするだけで、
            # 受信する処理が行われました。
            r.content

            print('■ 本文を受信したことを確認します。')
            # 隠し属性の r._content に、本文の
            # バイナリデータが入っているはずです。
            # その長さを表示してみます。
            print(f'type(r._content)): {type(r._content)}')
            print(f'len(r._content) {len(r._content)}\n')

            print('■ 普通の r.content 属性のほうも確認します。')
            print(f'type(r.content)): {type(r.content)}')
            print(f'len(r.content): {len(r.content)}\n')

            print('■ 接続が『閉じている』ことを確認します。')
            # r.content 属性にアクセスした後は、自動的に
            # 接続が閉じていました。
            # r.raw.closed 属性が True になっているはずです。
            print(f'type(r.raw) {type(r.raw)}')
            print(f'r.raw.closed {r.raw.closed} (← True のはず)\n')
        else:
            # 本文を取得せずに終了します。
            pass

    print('end')
    return

if __name__ == '__main__':
    main()

ところで、『デバッグモード』のウォッチ式で r.content を見てしまうと、そこで本文の受信が始まってしまいました。

なので、もし、『デバッグモード』で本文の受信タイミングを確認するときは、ウォッチ式で r.content を見ないようにします。

r.content 属性は、(Python) @property になっていて、本文を受信する処理が書かれていました。

(Requests) def content(self)

GET メソッドの実行結果

GET メソッドの実行結果です。

期待した通り、最初は『レスポンスヘッダー』だけを受信して、本文は取得していませんでした。

そして、レスポンスヘッダーの内容を確認してから、本文を受信することができました。

start

■ URL を決めます。
https://example.com/

■ GET メソッド (stream=True) で URL を開きます。

■ レスポンスヘッダーの内容を表示します。
type(r) <class 'requests.models.Response'>
type(r.headers) <class 'requests.structures.CaseInsensitiveDict'>
  Content-Encoding: gzip
  Age: 391489
  Cache-Control: max-age=604800
  Content-Type: text/html; charset=UTF-8
  Date: Tue, 23 Mar 2021 08:55:53 GMT
  Etag: "3147526947+gzip"
  Expires: Tue, 30 Mar 2021 08:55:53 GMT
  Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
  Server: ECS (sjc/4E76)
  Vary: Accept-Encoding
  X-Cache: HIT
  Content-Length: 648
(ヘッダの表示おわり)

■ 接続が『まだ閉じていない』ことを確認します。
r.raw.closed: False (← まだ False のはず)

■ まだ、『本文を受信していない』ことを確認します。
type(r._content)): <class 'bool'>
r._content: False (← まだ False のはず)

(r.content 属性にアクセスします)

■ 本文を受信したことを確認します。
type(r._content)): <class 'bytes'>
len(r._content) 1256

■ 普通の r.content 属性のほうも確認します。
type(r.content)): <class 'bytes'>
len(r.content): 1256

■ 接続が『閉じている』ことを確認します。
type(r.raw) <class 'urllib3.response.HTTPResponse'>
r.raw.closed True (← True のはず)

end

HEAD メソッドのコード例

HEAD メソッドで、『レスポンスヘッダー』だけを取得するコード例です。

"""head_method.py"""
import requests

def main():
    """メイン関数です。"""
    print('start\n')

    print('■ URL を決めます。')
    url = 'https://example.com/'
    print(f'{url}\n')

    print('■ HEAD メソッドで URL を開きます。\n')
    with requests.head(url, timeout=30) as r:
        print('■ レスポンスヘッダーの内容を表示します。')
        print(f'type(r) {type(r)}')
        print(f'type(r.headers) {type(r.headers)}')
        for (k, v) in r.headers.items():
            print(f'  {k}: {v}')
        print('(ヘッダの表示おわり)\n')

        print('■ 接続の状態を確認します。')
        print(f'r.raw.closed: {r.raw.closed}\n')

        print('■ r.content 属性の内容を表示します。')
        print(f'type(r.content)): {type(r.content)}')
        print(f'len(r.content): {len(r.content)} (← 0 のはず)\n')

    print('end')
    return

if __name__ == '__main__':
    main()

HEAD メソッドの実行結果

HEAD メソッドの実行結果です。

期待した通り、『レスポンスヘッダー』だけを受信して、本文は取得していませんでした。

また、接続は、レスポンスヘッダーを受信し終わった時点で、閉じていました。

あと、HEAD メソッドで取得したレスポンスヘッダーの内容は、GET メソッドの場合と比べて、少し異なっていました。

コード例の場合だと、HEAD メソッドの方は 'Content-Encoding' が無かったり、'Content-Length' が gzip で圧縮する前の長さになっていたりしました。

start

■ URL を決めます。
https://example.com/

■ HEAD メソッドで URL を開きます。

■ レスポンスヘッダーの内容を表示します。
type(r) <class 'requests.models.Response'>
type(r.headers) <class 'requests.structures.CaseInsensitiveDict'>
  Accept-Ranges: bytes
  Age: 65754
  Cache-Control: max-age=604800
  Content-Type: text/html; charset=UTF-8
  Date: Tue, 23 Mar 2021 09:18:21 GMT
  Etag: "3147526947"
  Expires: Tue, 30 Mar 2021 09:18:21 GMT
  Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
  Server: ECS (sec/96DC)
  X-Cache: HIT
  Content-Length: 1256
(ヘッダの表示おわり)

■ 接続の状態を確認します。
r.raw.closed: True

■ r.content 属性の内容を表示します。
type(r.content)): <class 'bytes'>
len(r.content): 0 (← 0 のはず)

end

以上です。

スポンサーリンク
ブログ
シェアする(押すとSNS投稿用の『編集ページ』に移動します)
フォローする(RSSフィードに移動します)
スポンサーリンク
シラベルノート
タイトルとURLをコピーしました