Python

【Python】RequestsでJSONデータと画像ファイルのPOST送信と取得

MAX

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してリスト、辞書型として扱える。

 

スポンサーリンク
ABOUT ME
MAX
MAX
ITエンジニア、データサイエンティスト
新卒でSIerに入社し、フリーランスになってWEB系へ転向。
その後AIの世界へ足を踏み入れ、正社員に戻る。 テーブルデータの分析がメイン。
スポンサーリンク
記事URLをコピーしました