【Python】RequestsでJSONデータと画像ファイルのPOST送信と取得
requestsはpost、getに加えてdelete等も分かりやすく実装できるので便利。
ファイルだけを送信する方法やJSONだけを送信する方法はたくさんの記事があるが、ファイルとJSONを同時に送信する方法があまり見かけなかったので記載。
結論から言うと、ファイルを送信する場合、requestsのヘッダーに「multipart/form-data
」を設定する必要があるため、JSONをJSONとして送れない。
受信側でJSONとして扱えないため、注意が必要。
フォームデータとしてであれば、1リクエストでファイルとデータを送信できる。
やり方は以下の通り。
画像ファイルとデータのPOST
1import requests
2
3#POSTするURL設定
4post_url = "http://xxx.xxx"
5
6#POSTするデータ設定
7data = {[{"data_col1": "data1-1",
8 "data_col2": "data2-1",
9 "data_col3": "data3-1"},
10 {"data_col1": "data1-2",
11 "data_col2": "data2-2",
12 "data_col3": "data3-2"},
13 {"data_col1": "data1-3",
14 "data_col2": "data2-3",
15 "data_col3": "data3-3"}
16 ]}
17json_data = { "col1": "val1",
18 "col2": "val2",
19 "col3": "val3",
20 "col4": data
21 }
22#POSTするファイルの読込
23files = { "image_file": open(file_path_name, 'rb') }
24
25#ヘッダー設定
26headers = { "Content-Type": "multipart/form-data" }
27
28#POST送信
29response = requests.post(
30 post_url,
31 data = json_data,
32 files = files,
33 headers = headers)
34
json.dumpsして送信していないので、受信側ではJSONとして扱えない。
以下のようにすれば、受信側でJSON(というか辞書型)として扱える。
フォームデータをJSONっぽく受け取る
1import ast
2
3#ファイルの取得
4file = request.files['image_file']
5
6#フォームの取得
7#この時点で、POST送信時のダブルクォーテーションは、
8#全てシングルクォーテーションになっているため、
9#json.loadsできない
10form_data = requests.form
11
12#個別の値の取得
13col1 = form_data.get('col1')
14col2 = form_data.get('col2')
15
16#form_data内のdataを全てリストとして取得
17data_arr = form_data.getlist('data')
18
19#リスト内の個別のdataに対して、処理を実施
20for data in data_arr:
21 #data(str型)を辞書型に変換
22 dict_data = ast.literal_eval(data)
23 #キーを指定して値を参照可能になる
24 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(リスト部分を文字列に変換してから送信)
1import requests
2import json
3
4#POSTするURL設定
5post_url = "http://xxx.xxx"
6
7#POSTするデータ設定
8data = {[{"data_col1": "data1-1",
9 "data_col2": "data2-1",
10 "data_col3": "data3-1"},
11 {"data_col1": "data1-2",
12 "data_col2": "data2-2",
13 "data_col3": "data3-2"},
14 {"data_col1": "data1-3",
15 "data_col2": "data2-3",
16 "data_col3": "data3-3"}
17 ]}
18#リストだけ事前に文字列に変換
19data = json.dumps(data)
20
21json_data = { "col1": "val1",
22 "col2": "val2",
23 "col3": "val3",
24 "col4": data
25 }
26#POSTするファイルの読込
27files = { "image_file": open(file_path_name, 'rb') }
28
29#ヘッダー設定
30headers = { "Content-Type": "multipart/form-data" }
31
32#POST送信
33response = requests.post(
34 post_url,
35 data = json_data,
36 files = files,
37 headers = headers)
json_data内のdata部分を事前にリストから文字列に変換して、POST送信する。
これで ast.literal_eval を使わずにdataを取得することが可能となる。
データ受信
1#ファイルの取得
2file = request.files['image_file']
3
4#フォームの取得
5form_data = request.form
6
7#個別の値取得
8col1 = form_data.get('col1')
9col2 = form_data.get('col2')
10
11#dataを取得
12data_arr = json.loads(form_data.get('data'))
13
14#リスト内の個別のdataに対して、処理を実施
15for data in data_arr:
16 #キーを指定して値を参照可能になる
17 print(data['data_col1'])
18
data部分は、送信時に事前にjson.dumpsで文字列に変換しているため、request.form.getした際にJSON形式の文字列として取得可能。
そのままjson.loadsしてリスト、辞書型として扱える。