Python

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

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