requestsはpost、getに加えてdelete等も分かりやすく実装できるので便利。
ファイルだけを送信する方法やJSONだけを送信する方法はたくさんの記事があるが、ファイルとJSONを同時に送信する方法があまり見かけなかったので記載。
結論から言うと、ファイルを送信する場合、requestsのヘッダーに「multipart/form-data
」を設定する必要があるため、JSONをJSONとして送れない。
受信側でJSONとして扱えないため、注意が必要。
フォームデータとしてであれば、1リクエストでファイルとデータを送信できる。
やり方は以下の通り。
画像ファイルとデータのPOST
import requests
#POSTするURL設定
post_url = "http://xxx.xxx"
#POSTするデータ設定
data = {[{"data_col1": "data1-1",
"data_col2": "data2-1",
"data_col3": "data3-1"},
{"data_col1": "data1-2",
"data_col2": "data2-2",
"data_col3": "data3-2"},
{"data_col1": "data1-3",
"data_col2": "data2-3",
"data_col3": "data3-3"}
]}
json_data = { "col1": "val1",
"col2": "val2",
"col3": "val3",
"col4": data
}
#POSTするファイルの読込
files = { "image_file": open(file_path_name, 'rb') }
#ヘッダー設定
headers = { "Content-Type": "multipart/form-data" }
#POST送信
response = requests.post(
post_url,
data = json_data,
files = files,
headers = headers)
json.dumpsして送信していないので、受信側ではJSONとして扱えない。
以下のようにすれば、受信側JSON(というか辞書型)として扱える。
フォームデータをJSONっぽく受け取る
import ast
#ファイルの取得
file = request.files['image_file']
#フォームの取得
#この時点で、POST送信時のダブルクォーテーションは、
#全てシングルクォーテーションになっているため、
#json.loadsできない
form_data = request.form
#個別の値の取得
col1 = form_data.get('col1')
col2 = form_data.get('col2')
#form_data内のdataを全てリストとして取得
data_arr = form_data.getlist('data')
#リスト内の個別のdataに対して、処理を実施
for data in data_arr:
#data(str型)を辞書型に変換
dict_data = ast.literal_eval(data)
#キーを指定して値を参照可能になる
print(dict_data['data_col1'])
リストを取得する場合には少し工夫が必要。
form.data.getlist(‘data’)で取得した値はstr型なので、辞書型に変換する必要があるが、ダブルクォーテーションがシングルクォーテーションに変換されているため、json.dumpsが使えない。
そこで、ast.literal_evalを使用して文字列から辞書型に変換する。 ast.literal_eval はnodeかstringを引数に取る。
ファイルとJSONデータを別々に送信しても問題ないなら、その方がオーソドックスなやり方になるため、良いと思う。
以下のように送信すれば、 ast.literal_evalを使用しなくても値取得が可能となる。
画像ファイルとデータのPOST(リスト部分を文字列に変換してから送信)
import requests
import json
#POSTするURL設定
post_url = "http://xxx.xxx"
#POSTするデータ設定
data = {[{"data_col1": "data1-1",
"data_col2": "data2-1",
"data_col3": "data3-1"},
{"data_col1": "data1-2",
"data_col2": "data2-2",
"data_col3": "data3-2"},
{"data_col1": "data1-3",
"data_col2": "data2-3",
"data_col3": "data3-3"}
]}
#リストだけ事前に文字列に変換
data = json.dumps(data)
json_data = { "col1": "val1",
"col2": "val2",
"col3": "val3",
"col4": data
}
#POSTするファイルの読込
files = { "image_file": open(file_path_name, 'rb') }
#ヘッダー設定
headers = { "Content-Type": "multipart/form-data" }
#POST送信
response = requests.post(
post_url,
data = json_data,
files = files,
headers = headers)
json_data内のdata部分を事前にリストから文字列に変換して、POST送信する。
これで ast.literal_eval を使わずにdataを取得することが可能となる。
データ受信
#ファイルの取得
file = request.files['image_file']
#フォームの取得
form_data = request.form
#個別の値取得
col1 = form_data.get('col1')
col2 = form_data.get('col2')
#dataを取得
data_arr = json.loads(form_data.get('data'))
#リスト内の個別のdataに対して、処理を実施
for data in data_arr:
#キーを指定して値を参照可能になる
print(data['data_col1'])
data部分は、送信時に事前にjson.dumpsで文字列に変換しているため、request.form.getした際にJSON形式の文字列として取得可能。
そのままjson.loadsしてリスト、辞書型として扱える。