Python, Requestsの使い方
Pythonの標準ライブラリurllibを使うとURLを開くことができるが、サードパーティライブラリのRequestsを使うとよりシンプルに書ける。
- Requests: HTTP for Humans — Requests 2.19.1 documentation
- Requests: 人間のためのHTTP — requests-docs-ja 1.0.4 documentation
サードパーティライブラリを自由にインストールできる環境であればurllibよりもRequestsを使うほうが楽。
ここではrequests.get()
とその返り値であるResponse
オブジェクトを中心に以下の内容について説明する。
- Requestsのインストール
- Requestsの基本的な使い方
requests.get()
Response
オブジェクト- url:
url
属性 - ステータスコード:
status_code
属性 - エンコーディング:
encoding
属性 - レスポンスヘッダ:
headers
属性 - テキスト:
text
属性 - バイナリデータ:
content
属性
- url:
- URLパラメータを指定: 引数
params
- リクエストヘッダ(カスタムヘッダ)を指定: 引数
headers
- リダイレクトの扱い
- JSONデータを取得・保存
- 画像やzipファイルなどをダウンロード
GET
だけでなくPOST
やDELETE
なども含めたWeb APIを利用する際の使い方は以下の記事を参照。
Requestsのインストール
pip
(環境によってはpip3
)でインストールできる。
$ pip install requests
Requestsの基本的な使い方
Requestsの基本的な使い方としてrequests.get()
とその返り値であるResponse
オブジェクトについて説明する。
requests.get()
requests.get()
は名前の通りHTTPのGET
メソッドに相当する。
ほかにrequests.post()
, requests.delete()
などもある。以下の記事を参照。
ここでは以下の例示用のドメインを使ってrequests.get()
を試す。
get()
の第一引数にURLを指定するとResponse
オブジェクトが取得できる。get()
のそのほかの引数については後述。
import requests
url = 'https://example.com/'
response = requests.get(url)
print(response)
# <Response [200]>
print(type(response))
# <class 'requests.models.Response'>
Responseオブジェクト
Response
オブジェクトの属性に様々な情報が格納されている。
url: url属性
url
属性でアクセスしたURLを取得できる。リダイレクトされた場合については後述。
print(response.url)
# https://example.com/
ステータスコード: status_code属性
status_code
属性でステータスコードを取得できる。
print(response.status_code)
# 200
ステータスコードの種類とその意味については以下のWikipediaのページを参照。
レスポンスヘッダ: headers属性
headers
属性でレスポンスヘッダを取得できる。
print(response.headers)
# {'Content-Encoding': 'gzip', 'Accept-Ranges': 'bytes', 'Cache-Control': 'max-age=604800', 'Content-Type': 'text/html', 'Date': 'Thu, 12 Jul 2018 11:58:54 GMT', 'Etag': '"1541025663"', 'Expires': 'Thu, 19 Jul 2018 11:58:54 GMT', 'Last-Modified': 'Fri, 09 Aug 2013 23:54:35 GMT', 'Server': 'ECS (oxr/8313)', 'Vary': 'Accept-Encoding', 'X-Cache': 'HIT', 'Content-Length': '606'}
headers
属性はCaseInsensitiveDict
という型。基本的には辞書(dict
型)だが、小文字と大文字を区別しないという特徴がある。
print(type(response.headers))
# <class 'requests.structures.CaseInsensitiveDict'>
print(response.headers['Content-Type'])
# text/html
print(response.headers['content-type'])
# text/html
print(response.headers['cOntEnt-typE'])
# text/html
[キー名]
で値を取得すると存在しないキーの場合はエラーKeyError
になるが、辞書と同様get()
メソッドを使うと存在しないキーに対してデフォルト値が返される。
# print(response.headers['xxxxx'])
# KeyError: 'xxxxx'
print(response.headers.get('xxxxx'))
# None
エンコーディング: encoding属性
encoding
属性でRequestsが推測したエンコーディングを取得できる。
print(response.encoding)
# ISO-8859-1
encoding
属性には任意の値を代入することが可能。
テキスト: text属性
上述のencoding
属性でデコードされたレスポンスの内容(文字列)はtext
属性で取得できる。
print(response.text)
# <!doctype html>
# <html>
# <head>
# <title>Example Domain</title>
#
# <meta charset="utf-8" />
# <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
# <meta name="viewport" content="width=device-width, initial-scale=1" />
# <style type="text/css">
# body {
# background-color: #f0f0f2;
# margin: 0;
# padding: 0;
# font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
#
# }
# div {
# width: 600px;
# margin: 5em auto;
# padding: 50px;
# background-color: #fff;
# border-radius: 1em;
# }
# a:link, a:visited {
# color: #38488f;
# text-decoration: none;
# }
# @media (max-width: 700px) {
# body {
# background-color: #fff;
# }
# div {
# width: auto;
# margin: 0 auto;
# border-radius: 0;
# padding: 1em;
# }
# }
# </style>
# </head>
#
# <body>
# <div>
# <h1>Example Domain</h1>
# <p>This domain is established to be used for illustrative examples in documents. You may use this
# domain in examples without prior coordination or asking for permission.</p>
# <p><a href="http://www.iana.org/domains/example">More information...</a></p>
# </div>
# </body>
# </html>
#
print(type(response.text))
# <class 'str'>
Webページの内容を取得したい場合はこのtext
属性を使う。
バイナリデータ: content属性
デコードされていないレスポンスの内容(バイト列)はcontent
属性で取得できる。
print(response.content)
# b'<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n <meta charset="utf-8" />\n <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\n <meta name="viewport" content="width=device-width, initial-scale=1" />\n <style type="text/css">\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 50px;\n background-color: #fff;\n border-radius: 1em;\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n body {\n background-color: #fff;\n }\n div {\n width: auto;\n margin: 0 auto;\n border-radius: 0;\n padding: 1em;\n }\n }\n </style> \n</head>\n\n<body>\n<div>\n <h1>Example Domain</h1>\n <p>This domain is established to be used for illustrative examples in documents. You may use this\n domain in examples without prior coordination or asking for permission.</p>\n <p><a href="http://www.iana.org/domains/example">More information...</a></p>\n</div>\n</body>\n</html>\n'
print(type(response.content))
# <class 'bytes'>
content
属性をencoding
属性のエンコーディングでデコードしたものはtext
属性と等価。
print(response.content.decode(response.encoding) == response.text)
# True
content
属性は画像やzipなどのテキストではないデータをダウンロードするときなどに使う。後述。
URLパラメータを指定: 引数params
URLの末尾に?
をつけてそのあとにkey=value
の形式で値を指定することでパラメータを指定することができる。このようなパラメータをURLパラメータやクエリパラメータ、その文字列をクエリ文字列(クエリストリング)などと呼ぶ。
例えば検索ワード「日本代表」の「ニュース」カテゴリのGoogle検索のURLは以下のようになる。
https://www.google.co.jp/search?q=%E6%97%A5%E6%9C%AC%E4%BB%A3%E8%A1%A8&tbm=nws
パラメータが複数ある場合は&
で接続し、日本語はパーセントエンコーディングする。
Requestsを使うと、辞書でパラメータを作成してget()
などの引数params
に指定することでURLパラメータを付与できる。Response
のurl
属性で正しく処理されていることが確認できる。
import requests
url = 'https://www.google.co.jp/search'
params = {'q': '日本代表', 'tbm': 'nws'}
r = requests.get(url, params=params)
print(r.url)
# https://www.google.co.jp/search?q=%E6%97%A5%E6%9C%AC%E4%BB%A3%E8%A1%A8&tbm=nws
なお、URLパラメータとして同じ名前のものを繰り返し指定する場合は辞書の値をリストにすればOK。
Pythonの標準ライブラリurllibでもURLパラメータの生成は可能。以下の記事を参照。
リクエストヘッダ(カスタムヘッダ)を指定: 引数headers
リクエストヘッダに情報を追加したい場合は引数headers
を使う。
例えばUser-Agent
に任意のユーザーエージェント文字列を指定すると、ユーザーエージェントを変更(偽装)できる。Yahoo! JAPANなどはユーザーエージェントによってレスポンスが異なる。
import requests
url = 'https://www.yahoo.co.jp/'
ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
headers = {'User-Agent': ua}
r_ua = requests.get(url, headers=headers)
詳しくは以下の記事を参照。
リダイレクトの扱い
指定したURLからリダイレクトされた場合、デフォルトでは最終的なページのレスポンスが返る。
例えば以下の例ではhttps://en.wikipedia.org
にアクセスしているが、返ってきたResponse
のurl
属性はリダイレクト先のhttps://en.wikipedia.org/wiki/Main_Page
になっている。
import requests
url = 'https://en.wikipedia.org'
r = requests.get(url)
print(r.url)
# https://en.wikipedia.org/wiki/Main_Page
print(r.status_code)
# 200
Response
のhistory
属性で、辿ってきた履歴をResponse
のリストとして取得できる。この例ではリダイレクトが1回だけなので要素数1のリストとなる。
print(r.history)
# [<Response [301]>]
print(len(r.history))
# 1
print(type(r.history[0]))
# <class 'requests.models.Response'>
履歴のResponse
の属性url
やstatus_code
でリダイレクト元のURLやステータスコードを確認できる。
print(r.history[0].url)
# https://en.wikipedia.org/
print(r.history[0].status_code)
# 301
この例では履歴が1つだけなのであまり意味ないが、リスト内包表記を使うと辿ってきたURLなどのリストを簡単に作成できて便利。
- 関連記事: Pythonリスト内包表記の使い方
print([response.url for response in r.history])
# ['https://en.wikipedia.org/']
get()
の引数allow_redirects=False
とするとリダイレクトを無効にすることができる。
r_not_redirect = requests.get(url, allow_redirects=False)
print(r_not_redirect.url)
# https://en.wikipedia.org/
print(r_not_redirect.status_code)
# 301
JSONデータを取得・保存
Web APIから取得するJSONデータはUnicodeエスケープされていたり面倒なことが多いが、Requestsならそれらの処理も簡単。
例として天気予報を取得できるWeb APIを使う。
引数params
でURLパラメータを指定する。レスポンスヘッダのContent-Type
を確認するとレスポンスがJSONだと確認できる。
import requests
import pprint
import json
url = 'http://weather.livedoor.com/forecast/webservice/json/v1'
params = {'city': 130010}
r = requests.get(url, params=params)
print(r.headers['Content-Type'])
# application/json; charset=utf-8
Response
オブジェクトのjson()
メソッドを使うとレスポンスの内容を辞書または辞書のリストに変換して取得できる。特に指定しなくても日本語も正しく扱われる。
pprintで適当に整形・省略して表示する。
json_data = r.json()
print(type(json_data))
# <class 'dict'>
pprint.pprint(json_data, depth=2, compact=True)
# {'copyright': {'image': {...},
# 'link': 'http://weather.livedoor.com/',
# 'provider': [...],
# 'title': '(C) LINE Corporation'},
# 'description': {'publicTime': '2018-07-12T18:33:00+0900',
# 'text': ' 日本の東には高気圧があって東に進んでいます。また、日本の南にも高気\n'
# '圧があり停滞しています。関東甲信地方は、高気圧の間で気圧の谷になって\n'
# 'います。\n'
# '\n'
# '【関東甲信地方】\n'
# ' 関東甲信地方はおおむね曇りで、雷を伴い非常に激しい雨の降っている所\n'
# 'があります。\n'
# '\n'
# ' 12日は、気圧の谷や湿った空気の影響により曇りで、所により雷を伴い\n'
# '非常に激しく降る所がある見込みです。\n'
# '\n'
# ' 13日は、上空の気圧の谷や湿った空気の影響で曇りますが、昼頃から次\n'
# '第に晴れるでしょう。朝晩は雨の降る所がある見込みです。\n'
# '\n'
# ' 関東近海では、13日にかけてうねりを伴って波がやや高いでしょう。ま\n'
# 'た、所々で霧が発生する見込みです。船舶は視程障害に注意してください。\n'
# '\n'
# '【東京地方】\n'
# ' 12日は、曇りですが、夜のはじめ頃まで雷を伴って激しく降る所がある\n'
# 'でしょう。\n'
# ' 13日は、曇りで昼過ぎから晴れますが、明け方まで雨の降る所がある見\n'
# '込みです。'},
# 'forecasts': [{...}, {...}, {...}],
# 'link': 'http://weather.livedoor.com/area/forecast/130010',
# 'location': {'area': '関東', 'city': '東京', 'prefecture': '東京都'},
# 'pinpointLocations': [{...}, {...}, {...}, {...}, {...}, {...}, {...}, {...},
# {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...},
# {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...},
# {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...},
# {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...},
# {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...},
# {...}, {...}, {...}, {...}, {...}],
# 'publicTime': '2018-07-12T17:00:00+0900',
# 'title': '東京都 東京 の天気'}
辞書なので任意の要素を取り出すのも簡単。
print(json_data['description']['text'])
# 日本の東には高気圧があって東に進んでいます。また、日本の南にも高気
# 圧があり停滞しています。関東甲信地方は、高気圧の間で気圧の谷になって
# います。
#
# 【関東甲信地方】
# 関東甲信地方はおおむね曇りで、雷を伴い非常に激しい雨の降っている所
# があります。
#
# 12日は、気圧の谷や湿った空気の影響により曇りで、所により雷を伴い
# 非常に激しく降る所がある見込みです。
#
# 13日は、上空の気圧の谷や湿った空気の影響で曇りますが、昼頃から次
# 第に晴れるでしょう。朝晩は雨の降る所がある見込みです。
#
# 関東近海では、13日にかけてうねりを伴って波がやや高いでしょう。ま
# た、所々で霧が発生する見込みです。船舶は視程障害に注意してください。
#
# 【東京地方】
# 12日は、曇りですが、夜のはじめ頃まで雷を伴って激しく降る所がある
# でしょう。
# 13日は、曇りで昼過ぎから晴れますが、明け方まで雨の降る所がある見
# 込みです。
pprint.pprint(json_data['forecasts'][0])
# {'date': '2018-07-12',
# 'dateLabel': '今日',
# 'image': {'height': 31,
# 'title': '曇り',
# 'url': 'http://weather.livedoor.com/img/icon/8.gif',
# 'width': 50},
# 'telop': '曇り',
# 'temperature': {'max': None, 'min': None}}
ファイルとして保存したい場合は標準ライブラリのjsonモジュールのjson.dump()
を使う。
with open('data/temp/download.json', 'w') as f:
json.dump(json_data, f, ensure_ascii=False, indent=4)
jsonモジュールの詳細は以下の記事を参照。
画像やzipファイルなどをダウンロード
画像やzipファイルなどのテキストではないデータをダウンロードすることも可能。
Response
のcontent
属性で取得できるバイナリデータをそのままバイナリとして保存するだけ。
画像の例。
import requests
import os
url_image = 'https://www.python.org/static/community_logos/python-logo.png'
r_image = requests.get(url_image)
print(r_image.headers['Content-Type'])
# image/png
filename_image = os.path.basename(url_image)
print(filename_image)
# python-logo.png
with open('data/temp/' + filename_image, 'wb') as f:
f.write(r_image.content)
zipファイルの例。
url_zip = 'http://www.post.japanpost.jp/zipcode/dl/oogaki/zip/13tokyo.zip'
r_zip = requests.get(url_zip)
print(r_zip.headers['Content-Type'])
# application/zip
filename_zip = os.path.basename(url_zip)
print(filename_zip)
# 13tokyo.zip
with open('data/temp/' + filename_zip, 'wb') as f:
f.write(r_zip.content)
いずれの場合もos.path.basename()
でURLからファイル名を抽出してその名前で保存している。
open()
によるファイルの読み書きについての詳細は以下の記事を参照。
標準ライブラリのurllibモジュールでも同様の処理が可能。以下の記事を参照。