『居善地』介面測試 — 5、使用Requests庫傳送POST請求

繁華似錦Fighting發表於2021-05-19

POST請求用於向伺服器提交資料,比如提交一個表單新建一個使用者、或修改一個使用者資訊等操作。

對於POST請求,我們可以通過瀏覽器開發者工具或者其他外部工具來進行抓包,得到請求的URL、請求頭(request headers)以及請求的表單data資訊,這三樣恰恰是我們用Requests庫模擬POST請求時需要的。

關於請求頭的配置和GET請求是一樣的,都是定義headers屬性即可。

而關於POST請求提交的引數,是和GET請求是不一樣的。

post請求四種傳送正文方式:

  • (1)請求正文是application/x-www-form-urlencoded
  • (2)請求正文是multipart/form-data
  • (3)請求正文是raw
  • (4)請求正文是binary

這四種提交資料的方式,是在請求頭Content-Type屬性中來定義。

1、請求正文是application/x-www-form-urlencoded

Reqeusts支援以application/x-www-form-urlencoded資料格式傳送POST請求(標準的POST請求資料格式,預設),只需要將請求的引數構造成一個字典,然後傳給requests.post()的data引數即可。

示例

"""
1.學習目標
    必須掌握requests庫傳送post請求方法
2.HTTP協議中post請求引數型別
    requests.post(url, data=None, json=None, **kwargs)
    根據不同的請求引數型別分為如下幾種:
        x-www-form-data-urlencoded
        raw_json格式
        form-data
        binary
3.json格式
    # 1.匯入requests庫
    # 2.明確請求地址
    # 3.明確請求引數
        data = {key:value}  字典格式
    # 4.傳送請求
        requests.post(url=url,json=data)
4.需求
    通過訪問http://httpbin.org/post介面,驗證post引數型別

"""
# 1.匯入requests庫
import requests
import json

# 2.明確請求地址
url = "http://httpbin.org/post"
# 3.明確請求引數
data = {
    "dep_id": "T01",
    "dep_name": "Test學院",
    "master_name": "Test-Master",
    "slogan": "Here is Slogan"
}
# 4.傳送請求
response = requests.post(url=url, data=data)

# 將python物件轉換為json字串(格式化返回資料)
result = json.dumps(response.json(), indent=2, ensure_ascii=False)
# print(type(result))  # 字串型別
print(result)

"""
返回結果:
{
  "args": {},
  "data": "",
  "files": {},
  ****************主要看這裡
  "form": {
    "dep_id": "T01",
    "dep_name": "Test學院",
    "master_name": "Test-Master",
    "slogan": "Here is Slogan"
  },
  ***********************
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "88",
    *****************主要看這裡
    "Content-Type": "application/x-www-form-urlencoded",
    *****************
    "Host": "httpbin.org",
    "User-Agent": "python-requests/2.18.4",
    "X-Amzn-Trace-Id": "Root=1-5ff401e3-1553596b7788e77e275c4772"
  },
  "json": null,
  "origin": "106.35.9.12",
  "url": "http://httpbin.org/post"
}
"""

說明

  • 傳送的請求中,form屬性接收了引數。
  • 在請求頭中,Content-Type屬性為application/x-www-form-urlencoded
  • 使用application/x-www-form-urlencoded格式傳送資料,requests.post(url=url, data=data)方法中一定要使用data變數來接收引數。
  • 換句話說資料格式是字典格式,使用data變數來接收,會預設傳送application/x-www-form-urlencoded資料格式的POST請求。(也可以在請求頭中明確一下Content-Type屬性,但沒必要。)

2、請求正文是raw

RAW的原意就是“未經加工”。換句話說RAW方式使用的是純字串的資料上傳方式,所以在傳送POST請求之前,可能需要手工的把一些JSON格式的資料轉換成字串的(加兩單引號),在進行提交。

RAW資料格式的POST請求有兩種:

  • 一種是xml格式文字(text/xml)。
  • 一種是json格式文字(application/json)。

(1)json格式文字(application/json)

# 1.匯入requests庫
import requests
import json

# 2.明確請求地址
url = "http://httpbin.org/post"
# 3.明確請求引數
data = {
    "data": [
        {
            "dep_id": "T01",
            "dep_name": "Test學院",
            "master_name": "Test-Master",
            "slogan": "Here is Slogan"
        }
    ]
}

# headers = {"Content-Type": "application/json"}

# 4.傳送請求
response = requests.post(url=url, json=data)
print(response)  # <Response [200]>
print(response.text)


"""
返回結果:
{
  "args": {}, 
  "data": "{\"data\": [{\"dep_id\": \"T01\", \"dep_name\": \"Test\\u5b66\\u9662\", \"master_name\": \"Test-Master\", \"slogan\": \"Here is Slogan\"}]}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "119", 
    **************************主要看這裡
    "Content-Type": "application/json", 
    **************************
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.18.4", 
    "X-Amzn-Trace-Id": "Root=1-5ff40a9d-6a6f19d272ba4c1b40ff7bbb"
  }, 
    **************************主要看這裡
  "json": {
    "data": [
      {
        "dep_id": "T01", 
        "dep_name": "Test\u5b66\u9662", 
        "master_name": "Test-Master", 
        "slogan": "Here is Slogan"
      }
    ]
  }, 
    **************************
  "origin": "106.35.9.12", 
  "url": "http://httpbin.org/post"
}
"""

說明

  • 傳送的請求中,json屬性接收了引數。
  • 在請求頭中,Content-Type屬性為application/json
  • 使用application/json格式傳送資料,requests.post(url=url, json=data)方法中一定要使用json變數來接收引數。
  • 換句話說資料格式是Json格式,使用json變數來接收,Requests會預設傳送application/json資料格式的POST請求。(也可以在請求頭中明確一下Content-Type屬性,但沒必要。)

注意:

這裡我們可以發現Requests模擬post請求時,請求頭格式為application/x-www-form-urlencoded與application/json的主要差別在於請求主體的構造格式(前者是鍵值對,後者是JSON串),前者直接用字典傳入,後者用json.dumps()函式將字典轉為JSON串即可。

也就是說在有需要的時候json模組下的dumps函式可以將dict轉換為str。

(2)xml格式文字(text/xml)

# 1.匯入requests庫
import requests
import json

# 2.明確請求地址
url = "http://httpbin.org/post"
# 3.明確請求引數
data = '<sites>' \
            '<site>' \
                '<name>菜鳥教程</name>' \
                '<url>www.runoob.com</url>' \
            '</site>' \
            '<site>' \
                '<name>Google</name>' \
                '<url>www.google.com</url>' \
            '</site>' \
       '</sites>'

#  requests.post方法中適用json變數來接收資料,
# 預設是"Content-Type": "application/json", 
# 這裡我們需要重新宣告一下Content-Type屬性。
headers = {'Content-type': 'text/xml'}

# 4.傳送請求
# 如果資料用data變數來接收會報錯。
response = requests.post(url=url, json=data, headers=headers)
print(response)  # <Response [200]>
# print(response.text)

# 將python物件轉換為json字串(格式化返回資料)
result = json.dumps(response.json(), indent=2, ensure_ascii=False)
# print(type(result))  # 字串型別
print(result)

"""
返回結果:
{
  "args": {},
  "data": "\"<sites><site><name>\\u83dc\\u9e1f\\u6559\\u7a0b</name><url>www.runoob.com</url></site><site><name>Google</name><url>www.google.com</url></site></sites>\"",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "149",
    **************************主要看這裡
    "Content-Type": "text/xml",
    **************************
    "Host": "httpbin.org",
    "User-Agent": "python-requests/2.18.4",
    "X-Amzn-Trace-Id": "Root=1-5ff40fa5-21a79b532b1ccf6d20173fd7"
  },
  **************************主要看這裡
  "json": "<sites><site><name>菜鳥教程</name><url>www.runoob.com</url></site><site><name>Google</name><url>www.google.com</url></site></sites>",
    **************************
  "origin": "106.35.9.12",
  "url": "http://httpbin.org/post"
}
"""

說明

  • text/xml格式相對用的少。
  • xml也可以作為一個檔案來傳輸。
  • 需要重新宣告請求頭中Content-Type屬性。
  • 其他和application/json一樣。

提示:其實raw格式資料可以上傳text、json、xml、html等純字元的文字。

3、正文是binary

使用binary格式的正文傳送POST請求,是直接使用二進位制流進行資料傳輸,多用於上傳單個圖片或圖片。

也可以用於把請求的引數放入一個檔案中,進行資料的提交。

示例如下:

"""
1.學習目標
    掌握requests傳送post請求
2.HTTP協議中post請求引數型別
    x-www-form-data-urlencoded
    raw_json格式
    form-data
    binary
3.binary格式
    # 1.明確請求地址
    # 2.明確請求引數
        data = {"files":open("檔名","rb")}  字典格式
    # 3.傳送請求
        requests.post(url= url,files=data)
4.需求
    http://httpbin.org/post
"""
# 1.匯入requests
import requests

# 2.請求地址
url = "http://httpbin.org/post"

# 3.請求引數
# 讀取檔案中的資料作為引數進行提交。
# key位置要寫files,是規範
# 也可以寫其他的名字,不規範
data = {"files": open("test.txt", "rb")}

# 4.傳送請求
response = requests.post(url=url, files=data)
print(response.text)


"""
請求結果如下:
{
  "args": {}, 
  "data": "", 
  **************************主要看這裡
  "files": {
    "files": "username=\u5927\u5c0f\u59d0\r\npassword=test123456\r\nage=18"
  }, 
  **************************
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "192", 
    **************************主要看這裡
    "Content-Type": "multipart/form-data; boundary=351e0b73ea144694a9e9fdd1e10d2486", 
    **************************
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.18.4", 
    "X-Amzn-Trace-Id": "Root=1-5ff499ea-7ad42c4e6f056b44347b3c26"
  }, 
  "json": null, 
  "origin": "106.35.9.12", 
  "url": "http://httpbin.org/post"
}
"""

說明

  • 傳送的請求中,files屬性接收了引數。
  • 在請求頭中,Content-Type屬性為multipart/form-data
  • 使用application/json格式傳送資料,requests.post(url=url, files=data)方法中一定要使用files變數來接收引數。
  • 換句話說Requests也支援傳送binary資料形式的POST請求,只需將檔案傳給requests.post()方法的files引數即可。

4、請求正文是multipart/form-data

multipart/form-data資料格式的POST請求,多用於檔案上傳。

示例1:上傳檔案

"""
1.學習目標
    掌握requests傳送post請求
2.HTTP協議中post請求引數型別
    x-www-form-data-urlencoded
    raw_json格式
    form-data
    binary
3.form-data格式
    使用files變數來接收資料,預設是使用form-data格式傳送POST請求。
4.需求
    http://httpbin.org/post
"""

import requests

files = {'file1': open('logo.png', 'rb')}
response = requests.post('http://127.0.0.1:9999/post', files=files)
print(response.text)


"""
介面返回結果:
{
  "args": {}, 
  "data": "", 
  **************************主要看這裡
  "files": {
    "file1": "data:application/octet-stream;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAgGBgcGBQgHerOCtwJPHpvQjoYqxmHighE/wO1YuuATgOKt9wGMd653WXlhe2xbcpauJjePjQYuZTOOk032eaYj+GgOQ+E1QCBj4UxtunNUFIjBmm5P05oBjLv99qoKgEpW9PSu1b0tAglXYOr2/uN4rtp6ZZay53n81IAlauN/pRH/2Q=="
  }, 
  **************************
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Connection": "keep-alive", 
    "Content-Length": "394145", 
    **************************主要看這裡
    "Content-Type": "multipart/form-data; boundary=4efea05a2cf34e78a75508a1ebf000ec", 
    **************************
    "Host": "127.0.0.1:9999", 
    "User-Agent": "python-requests/2.18.4"
  }, 
  "json": null, 
  "origin": "127.0.0.1", 
  "url": "http://127.0.0.1:9999/post"
}
"""

示例2:上傳檔案並重新命名

我們也可以顯式地設定檔名,檔案型別和請求頭:

import requests
# hangge.png 為圖片名稱
files = {'file1': ('hangge.png', open('logo.png', 'rb'), 'image/png', {'Expires': '0'})}
response = requests.post('http://127.0.0.1:9999/post', files=files)
print(response.text)


"""
介面返回結果:
{
  "args": {}, 
  "data": "", 
  **************************主要看這裡
  "files": {
    "file1": "data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyacMkc0Yyc0UAf/Z"
  }, 
  **************************
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Connection": "keep-alive", 
    "Content-Length": "7063", 
    **************************主要看這裡
    "Content-Type": "multipart/form-data; boundary=382e06cba6834118a1f1efd0ea2c45e3", 
    **************************
    "Host": "127.0.0.1:9999", 
    "User-Agent": "python-requests/2.18.4"
  }, 
  "json": null, 
  "origin": "127.0.0.1", 
  "url": "http://127.0.0.1:9999/post"
}

"""

示例3:多檔案上傳

有時我們需要在一個請求中同時傳送多個檔案,同樣使用 files 引數傳入一個陣列即可:

import requests
files = [
        ('file1', ('1.png', open('logo.png', 'rb'), 'image/png')),
        ('file2', ('2.png', open('logo.png', 'rb'), 'image/png'))
]
response = requests.post('http://127.0.0.1:9999/post', files=files)
print(response.text)


"""
介面返回結果:
{
  "args": {}, 
  "data": "", 
  **************************主要看這裡
  "files": {
    "file1": "data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgc3p8zdacMkc0Yyc0UAf/Z", 
    "file2": "data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgc3p8zdacMkc0Yyc0UAf/Z"
  }, 
  **************************
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Connection": "keep-alive", 
    "Content-Length": "14054", 
    **************************主要看這裡
    "Content-Type": "multipart/form-data; boundary=ba662835a2364b069c99ba3ffa56b974", 
    **************************
    "Host": "127.0.0.1:9999", 
    "User-Agent": "python-requests/2.18.4"
  }, 
  "json": null, 
  "origin": "127.0.0.1", 
  "url": "http://127.0.0.1:9999/post"
}

"""

示例4:上傳時附帶其它引數

如果我們需要在上傳檔案的同時傳遞一些其它引數,也是可以的:

import requests

data = {
    "name": "ABC.com",
    "age": 100
}
files = [
    ('file1', ('1.png', open('logo.png', 'rb'), 'image/png')),
    ('file2', ('2.png', open('logo.png', 'rb'), 'image/png'))
]
response = requests.post('http://127.0.0.1:9999/post', data=data, files=files)
print(response.text)

"""
介面返回結果:
{
  "args": {}, 
  "data": "", 
  **************************主要看這裡
  "files": {
    "file1": "data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDA0Yyc0UAf/Z", 
    "file2": "data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDA0Yyc0UAf/Z"
  }, 
  "form": {
    "age": "100", 
    "name": "ABC.com"
  }, 
  **************************
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Connection": "keep-alive", 
    "Content-Length": "14233", 
    **************************主要看這裡
    "Content-Type": "multipart/form-data; boundary=6bdedbde2b48465683ef4e3451f7e015", 
    **************************
    "Host": "127.0.0.1:9999", 
    "User-Agent": "python-requests/2.18.4"
  }, 
  "json": null, 
  "origin": "127.0.0.1", 
  "url": "http://127.0.0.1:9999/post"
}
"""

示例5:流式上傳檔案

  • 有時我們需要上傳一個非常大的檔案(比如 1G 左右),如果像上面的方式直接使用 Requests 提交,可能會造成記憶體不足而崩潰。

  • 所以傳送大檔案時還是建議將請求做成資料流。不過預設下 Requests 不支援流式上傳,但有個第三方包 requests-toolbelt 是支援的(本質還是 multipart/form-data 上傳)

  • 在使用 requests-toolbelt 之前,我們首先通過 pip 進行安裝:

    # cmd命令列終端執行如下命令。
    pip install requests-toolbelt
    

示例如下

"""
1.學習目標
    掌握requests傳送post請求
2.HTTP協議中post請求引數型別
    x-www-form-data-urlencoded
    raw_json格式
    form-data
    binary
3.form-data格式
    # 1.匯入requests庫,requests_toolbelt庫
    # 2.明確請求地址
    # 3.明確請求引數
        data = {}  字典格式
        對請求引數加工(例項化)
        m = MultipartEncoder(fields = data)
    # 4.新增請求頭
        headers = {"Content_Type":m.content_type}
    # 5.傳送請求
        requests.post(url= url,data=m,headers=headers)
4.需求
    http://httpbin.org/post
"""
# 1.匯入requests庫
import requests
from requests_toolbelt import MultipartEncoder
# from requests_toolbelt.multipart.encoder import MultipartEncoder

# 2.明確請求地址
url = "http://httpbin.org/post"
# 3.明確請求引數
data = {
    "username": "Jerry",
    "password": "1232456",
    "sex": "男"
}

# requests-toolbelt 還提供了個監視器(MultipartEncoderMonitor),
# 該監視器接受一個回撥函式,我們可以在回撥中實時跟蹤進度。
# from requests_toolbelt import MultipartEncoderMonitor
# def my_callback(monitor):
#     progress = (monitor.bytes_read / monitor.len) * 100
#     print("\r 檔案上傳進度:%d%%(%d/%d)"
#           % (progress, monitor.bytes_read, monitor.len), end=" ")

# m = MultipartEncoder(
#     fields={'name': 'ABC.com', "age": '100',
#             'file1': ('1.png', open('logo.png', 'rb'), 'image/png'),
#             'file2': ('2.png', open('logo.png', 'rb'), 'image/png')}
# )

# 4.新增請求頭和加工請求引數
# 加工請求引數----讓每個引數都要帶有邊界
m = MultipartEncoder(fields=data)

# 新增請求頭
headers = {"Content_Type": m.content_type}

# 4.傳送請求
response = requests.post(url=url, data=m, headers=headers)
print(response.text)

"""
請求結果:

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  **************************主要看這裡
  "form": {    
    "username": "Jerry",
    "password": "1232456",
    "sex": "男"
    }, 
  **************************
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "192", 
    **************************主要看這裡
    "Content-Type": "multipart/form-data; boundary=351e0b73ea144694a9e9fdd1e10d2486", 
    **************************
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.18.4", 
    "X-Amzn-Trace-Id": "Root=1-5ff499ea-7ad42c4e6f056b44347b3c26"
  }, 
  "json": null, 
  "origin": "106.35.9.12", 
  "url": "http://httpbin.org/post"
}
"""

5、總結Binary和Form-data區別

主要區別在於:

  • Binary只可以上傳二進位制資料,通常用來上傳檔案,由於沒有鍵值,所以一次只能上傳一個檔案,而Form-data可以傳多個。
  • Form-data既可以上傳檔案等二進位制資料,也可以上傳表單鍵值對。利用key-value對,可以分別對每個檔案進行描述。

參考:https://www.hangge.com/blog/cache/detail_2375.html

相關文章