requests库post请求参数data、json和files的使用

1. 内容

主要讨论使用requests.post函数的参数data、json和files来发送不同类型的数据或者文件。

2. 环境

python:3.9.12
requests:2.20.0

3. requests.post

主要关注post请求中的data、json以及files参数。首先,查看requests.data的源码,data和json参数的内容如下:

def post(url, data=None, json=None, **kwargs):
    r"""Sends a POST request.

    :param url: URL for the new :class:`Request` object.
    :param data: (optional) Dictionary, list of tuples, bytes, or file-like
        object to send in the body of the :class:`Request`.
    :param json: (optional) json data to send in the body of the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """
    return request('post', url, data=data, json=json, **kwargs)

继续查看requests.request的源码,主要关注参数files的说明,如下:

def request(method, url, **kwargs):
    """Constructs and sends a :class:`Request <Request>`.

    ...
    :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
        ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
        or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
        defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
        to add for the file.
    """

在了解data、json以及files参数的取值范围之后,接下来就是以实际的例子来说明分别识别data、json以及files发送数据的情况。

4. data

通过requests.data参数可以看到,data的参数支持4种类型,分别为Dictionary、list of tuples、bytes、file-like object,这里主要关注使用Dictionary、list of tuples这两种方式发送数据时的请求方式、请求信息和响应结果信息。剩余的bytes、file-like object会在之后遇到实际应用时补充。

4.1. Dictionary

首先,使用字典的形式提交数据,如下所示:

  1. 定义一个Python字典:data,并以键值对的形式说明需要上传的数据。
  2. 通过输出请求头的body信息res.request.body发现,其取值为name=Tom&age=18.
  3. 使用data传递Dictionary类型的键值对时,此时的Content-Type默认为application/x-www-form-urlencoded
import requests

url = "http://httpbin.org/post"
data = {
    "name": "Tom",
    "age": 18
}
res = requests.post(url, data=data)
print("请求头", res.request.headers, type(res.request.headers))
print("请求体", res.request.body, type(res.request.body))
print("响应头", res.headers, type(res.headers))
print("响应体", res.text, type(res.text))

输出结果为:

请求头 {'User-Agent': 'python-requests/2.20.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '15', 'Content-Type': 'application/x-www-form-urlencoded'} <class 'requests.structures.CaseInsensitiveDict'>
请求体 name=Tom&age=18 <class 'str'>
响应头 {'Date': 'Tue, 17 May 2022 11:12:41 GMT', 'Content-Type': 'application/json', 'Content-Length': '496', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true'} <class 'requests.structures.CaseInsensitiveDict'>
响应体 {
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "age": "18", 
    "name": "Tom"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "15", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.20.0", 
    "X-Amzn-Trace-Id": "Root=1-62838329-3e9cc2f465f24f73694a0f1a"
  }, 
  "json": null, 
  "origin": "58.48.122.206", 
  "url": "http://httpbin.org/post"
}
 <class 'str'>

4.1.1. 源码-分析requests是如何对传入的data数据进行处理的

首先,使用pycharm工具,一步步查看代码调用顺序,找到修改data数据的代码即可,这里可自己查看的调用顺序整理如下:(api.py::post带包api.py文件中的post函数)

调用requests.post时,此时相当于执行了requests/api.py::post函数
接着调用requests/api.py::request函数
接着会调用requests/sessions.py::request函数
在该函数中执行了这个语句prep = self.prepare_request(req),继续查看prepare_request()函数
在该函数中调用了requests/models.py/prepare函数
接着在该函数中调用了requests/models.py/prepare_body(data, files, json),可以看到这里对data数据进行了处理。

这里主要关注models.py文件中的prepare_body和_encode_params函数,下面列举出该函数的部分内容,如下:

  1. 在prepare_body函数中,此时当data存在值的时候,会调用self._encode_params(data),也就是在调用_encode_params的时候将data = {"name": "Tom","age": 18}转化为了name=Tom&age=18。(这里就不在深究requests是如何实现的了)
  def prepare_body(self, data, files, json=None):
      """Prepares the given HTTP body data."""

      # Check if file, fo, generator, iterator.
      # If not, run through normal process.

      # Nottin' on you.
      body = None
      content_type = None

      if is_stream:
          ...
      else:
          ...
          else:
              if data:
                  body = self._encode_params(data)
                  if isinstance(data, basestring) or hasattr(data, 'read'):
                      content_type = None
                  else:
                      content_type = 'application/x-www-form-urlencoded'

4.2. list of tuples

使用元组列表构建一个data,然后传递给post请求的data参数中,如下所示:

  1. 使用元组构建data的方式也比较简单,其实就是将data转化为list,每个键值对变成list中的一个元组,即可以通过list(zip(data.keys(), data.values()))将data转化为元组列表。
import requests

url = "http://httpbin.org/post"
data = [("name", "Tom"), ("age", 18)]
res = requests.post(url, data=data)
print("请求头", res.request.headers, type(res.request.headers))
print("请求体", res.request.body, type(res.request.body))

输出结果如下,可以发现与使用Dictionary的方式一样。

请求头 {'User-Agent': 'python-requests/2.20.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '15', 'Content-Type': 'application/x-www-form-urlencoded'} <class 'requests.structures.CaseInsensitiveDict'>
请求体 name=Tom&age=18 <class 'str'>

5. json

通过requests.post中对json参数的说明:

:param json: (optional) json data to send in the body of the :class:`Request`.

在python中,不存在json数据类型。在使用json参数的时候,可以直接传递一个Dictionary类型的数据,requests会自动将其转化为json并使用utf-8编码为字节。比如下面的例子:

  1. 将Dictionary类型的数据传递给json参数,此时输出请求体res.request.body为b'{"name": "Tom", "age": 18}',即requests将字典转化为了json字符串并编码为字节了。
  2. 使用json发送数据的时候,此时默认采用的content-type为application/json(可以通过res.request.headers看到请求采用的content-type类型)
import requests
import json

url = "http://httpbin.org/post"
data = {
    "name": "Tom",
    "age": 18
}

res = requests.post(url, json=data)
print("请求头", res.request.headers, type(res.request.headers))
print("请求体", res.request.body, type(res.request.body))

输出结果为:

请求头 {'User-Agent': 'python-requests/2.20.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '26', 'Content-Type': 'application/json'} <class 'requests.structures.CaseInsensitiveDict'>
请求体 b'{"name": "Tom", "age": 18}' <class 'bytes'>

如上,res.request.body返回的是一个bytes类型。这里可以通过查看源码来看requests在接收到json参数传递的数据之后做了哪些操作。

5.1. 源码-分析requests是如何对传入的json数据进行处理的

和前面分析data数据一样,这里直接查看requests/models.py/prepare_body函数,该函数的部分内容如下所示:

def prepare_body(self, data, files, json=None):
    """Prepares the given HTTP body data."""

    # Check if file, fo, generator, iterator.
    # If not, run through normal process.

    # Nottin' on you.
    body = None
    content_type = None

    if not data and json is not None:
        # urllib3 requires a bytes-like body. Python 2's json.dumps
        # provides this natively, but Python 3 gives a Unicode string.
        content_type = 'application/json'
        body = complexjson.dumps(json)
        if not isinstance(body, bytes):
            body = body.encode('utf-8')

如上,可以看到当data为空且json不为None的时候,此时设置了请求类型为application/json,同时将参数转化为json字符串,并使用utf-8编码为bytes类型。

6. files

首先,根据requests.request函数中的参数说明(具体可以参考第3节),对于files参数,可以有以下几种类型:

  1. Dictionary of 'name': file-like-objects
  2. {‘name’: file-tuple}:file-tuple又可以包含多种,比如:
    1. 2-tuple:分别是filename, fileobj
    2. 3-tuple:分别是filename, fileobj, content_type
    3. 4-tuple:分别是filename, fileobj, content_type, custom_headers

注意:

  1. 这里的name相当于前端通过form表单上传文件时,input字段的name的取值,即后台会根据这里的name获取上传的文件。
<input type="file" name="file" id="file">

6.1. 采用{'name', file-like-objects}上传文件

#coding=utf-8
# 采用xampp搭建的真实服务测试过,可以上传文件成功
import requests

url = "http://httpbin.org/post"
files = {
    "file": open("./second_module.py", "rb")
}
res = requests.post(url, files=files)
print("请求头", res.request.headers, type(res.request.headers))
print("请求体", res.request.body, type(res.request.body))
print("响应头", res.headers, type(res.headers))
print("响应体", res.text, type(res.text))

输出结果如下:

请求头 {'User-Agent': 'python-requests/2.20.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '175', 'Content-Type': 'multipart/form-data; boundary=60676effe8eb282f2238484ce8c733d7'} <class 'requests.structures.CaseInsensitiveDict'>
请求体 b'--60676effe8eb282f2238484ce8c733d7\r\nContent-Disposition: form-data; name="file"; filename="second_module.py"\r\n\r\nimport first_module\r\n\r\n\r\n--60676effe8eb282f2238484ce8c733d7--\r\n' <class 'bytes'>
响应头 {'Date': 'Wed, 18 May 2022 09:33:48 GMT', 'Content-Type': 'application/json', 'Content-Length': '532', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true'} <class 'requests.structures.CaseInsensitiveDict'>
响应体 {
  "args": {}, 
  "data": "", 
  "files": {
    "file": "import first_module\r\n\r\n"
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "175", 
    "Content-Type": "multipart/form-data; boundary=60676effe8eb282f2238484ce8c733d7", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.20.0", 
    "X-Amzn-Trace-Id": "Root=1-6284bd7c-4834506703ccab59771b3319"
  }, 
  "json": null, 
  "origin": "58.48.122.206", 
  "url": "http://httpbin.org/post"
}
 <class 'str'>

如上:

  1. 使用files上传文件时,此时默认的content-type类型为:multipart/form-data; boundary=60676effe8eb282f2238484ce8c733d7,即会自动生成1个随机的boundary。
  2. 请求体中包含如下内容.
b'--60676effe8eb282f2238484ce8c733d7\r\nContent-Disposition: form-data; name="file"; filename="second_module.py"\r\n\r\nimport first_module\r\n\r\n\r\n--60676effe8eb282f2238484ce8c733d7--\r\n' 

6.2. 采用{'name': file-tuple}的形式上传文件

#coding=utf-8
# 采用xampp搭建的真实服务测试过,可以上传文件成功
import requests
import mimetypes

url = "http://httpbin.org/post"

filepath = "./second_module.py"
# 猜测文件类型
file_type = mimetypes.guess_type("./second_module.py")[0]      # image/jpeg

files = {
    # 元组中第一个参数:代表实际上传服务器时使用的文件名,没有传入该参数的时候,实际上传之后的filename是open函数中打开的文件的文件名。
    # 元祖第二个参数:就是一个文件对象
    # 元祖第三个参数:文件类型,这里通过使用mimetypes来猜测文件类型。
    # 元祖第四个参数:自定义的一些头部信息
    "file": ("new_filename.py", open(filepath, "rb"), file_type, {'Expires':'0'})
}
res = requests.post(url, files=files)
print("请求头", res.request.headers, type(res.request.headers))
print("请求体", res.request.body, type(res.request.body))

此时的输出结果为:

请求头 {'User-Agent': 'python-requests/2.20.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '215', 'Content-Type': 'multipart/form-data; boundary=98ef3ae8047229f3a0a34c3ad0b51004'} <class 'requests.structures.CaseInsensitiveDict'>
请求体 b'--98ef3ae8047229f3a0a34c3ad0b51004\r\nContent-Disposition: form-data; name="file"; filename="new_filename.py"\r\nContent-Type: text/x-python\r\nExpires: 0\r\n\r\nimport first_module\r\n\r\n\r\n--98ef3ae8047229f3a0a34c3ad0b51004--\r\n' <class 'bytes'>

6.3. 上传多个文件

import requests
import mimetypes

url = "http://httpbin.org/post"

filepath = "./second_module.py"
file_type = mimetypes.guess_type("./second_module.py")[0]      # image/jpeg

files = [
    ("file", ("new_filename1.py", open(filepath, "rb"), file_type, {'Expires':'0'})),
    ("file2", ("new_filename2.py", open(filepath, "rb"), file_type, {'Expires':'0'})),
]

res = requests.post(url, files=files)
print("请求头", res.request.headers, type(res.request.headers))
print("请求体", res.request.body, type(res.request.body))

输出结果如下:

请求头 {'User-Agent': 'python-requests/2.20.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '394', 'Content-Type': 'multipart/form-data; boundary=fe0a1f7e51c9795b4665897765267b7c'} <class 'requests.structures.CaseInsensitiveDict'>
请求体 b'--fe0a1f7e51c9795b4665897765267b7c\r\nContent-Disposition: form-data; name="file"; filename="new_filename1.py"\r\nContent-Type: text/x-python\r\nExpires: 0\r\n\r\nimport first_module\r\n\r\n\r\n--fe0a1f7e51c9795b4665897765267b7c\r\nContent-Disposition: form-data; name="file"; filename="new_filename2.py"\r\nContent-Type: text/x-python\r\nExpires: 0\r\n\r\nimport first_module\r\n\r\n\r\n--fe0a1f7e51c9795b4665897765267b7c--\r\n' <class 'bytes'>

说明:

  1. 上传多个文件时,可以使用上面例子中提到的元组列表的形式,也可以使用下面的形式
files = {
    "file": ("new_filename3.py", open(filepath, "rb"), file_type, {'Expires':'0'}),
    "file2": ("new_filename4.py", open(filepath, "rb"), file_type, {'Expires':'0'}),
}

7. 提交application/json类型的数据的两种方式

7.1. 方式1:使用data参数

此时需要使用json.dumps(data)将Dictionary类型转换为json字符串,并在请求头中添加content-type字段类型为application/json

import requests

url = "http://httpbin.org/post"
data = {
    "name": "Tom",
    "age": 18
}
res = requests.post(url, data=json.dumps(data), headers={"content-type":"application/json"})

print("请求头", res.request.headers, type(res.request.headers))
print("请求体", res.request.body, type(res.request.body))

此时的输出结果如下:

请求头 {'User-Agent': 'python-requests/2.20.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'content-type': 'application/json', 'Content-Length': '26'} <class 'requests.structures.CaseInsensitiveDict'>
请求体 {"name": "Tom", "age": 18} <class 'str'>

7.2. 方式2:使用json参数

直接参考第5节。

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
答:Python requests.post参数是指使用requests发送HTTP POST请求时,可以指定的参数。当使用 Pythonrequests 进行 POST 请求时,可以通过传递参数来定制请求的行为。下面是一些常用的参数: 1. url: 需要访问的 URL 地址 2. data: 需要 POST 的数据,可以是一个字典、元组列表或者文件对象 3. json: 以 JSON 编码的数据,如果传递了该参数,则 data 参数会被忽略 4. headers: 字典类型,HTTP 请求的头部信息 5. auth: HTTP 基本身份验证,可以是一个元组,例如 ('username', 'password') 6. cookies: 字典类型,发送请求时的 cookies 7. timeout: 设定超时时间,以秒为单位 8. allow_redirects: 布尔类型,是否允许重定向,默认为 True 9. proxies: 字典类型,设置代理服务器 10. verify: 布尔类型,是否验证 SSL 证书 11. files: 字典类型,需要上传的文件,可以使用元组列表传递多个文件 12. stream: 布尔类型,是否立即下载响应内容,默认为 False 下面是一个示例代码: ```python import requests url = 'http://httpbin.org/post' data = {'username': 'testuser', 'password': 'testpass'} response = requests.post(url, data=data) print(response.status_code) print(response.text) ``` 该示例代码发送一个 POST 请求到 http://httpbin.org/post,并携带了一个名为 data参数,值为 {'username': 'testuser', 'password': 'testpass'}。服务器会将请求的内容封装成一个 JSON 格式的响应,并返回给客户端。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值