【python介面自動化】- 使用requests庫傳送http請求

miki_peng發表於2020-08-16

前言:什麼是Requests ?Requests 是⽤Python語⾔編寫,基於urllib,採⽤Apache2 Licensed開源協議的 HTTP 庫。它⽐ urllib 更加⽅便,可以節約我們⼤量的⼯作,完全滿⾜HTTP測試需求。

安裝requests庫

​ cmd命令列執行pip install requests

HTTP 請求方法

​ HTTP 協議 (Hyper Text Transfer Protocol),一個基於TCP/IP通訊協議來傳遞資料,包括html檔案、影像、結果等,即是一個客戶端和伺服器端請求和應答的標準。根據HTTP 標準,HTTP 請求可以使用多種請求方法。

  • HTTP 0.9:只有基本的文字GET請求,沒有固定的版本號,不支援請求頭。
  • HTTP 1.0:完善的請求/響應模型,並將協議補充完整,定義了三種請求方法:GETPOSTHEAD方法。
  • HTTP 1.1:在1.0的基礎上進行更新,新增了五種請求方法:OPTIONSPUTDELETETRACECONNECT方法。
  • HTTP 2.0(未普及):請求/響應首部的定義基本沒有改變,只是所有首部鍵必須全部小寫,而且請求行要獨立為:method:scheme:host:path這些鍵值對。
序號 方法 描述
1 GET 請求指定的頁面資訊,並返回實體主體。
2 HEAD 類似於 GET 請求,只不過返回的響應中沒有具體的內容,用於獲取報頭。
3 POST 向指定資源提交資料進行處理請求(例如提交表單或者上傳檔案),資料被包含在請求體中。POST 請求可能會導致新的資源的建立和/或已有資源的修改。
4 PUT 從客戶端向伺服器傳送的資料取代指定的文件的內容。
5 DELETE 請求伺服器刪除指定的頁面。
6 CONNECT HTTP/1.1 協議中預留給能夠將連線改為管道方式的代理伺服器。
7 OPTIONS 允許客戶端檢視伺服器的效能。
8 TRACE 回顯伺服器收到的請求,主要用於測試或診斷。
9 PATCH 是對 PUT 方法的補充,用來對已知資源進行區域性更新 。

HTTP請求常用的Get和Post兩種方法:

  • ? GET是從伺服器上獲取資料,POST是向伺服器傳送資料
  • ? GET請求引數都顯示在瀏覽器網址上,HTTP伺服器根據該請求所包含URL中的引數來產生響應內容,也就是說GET請求的引數是URL的一部分。比如百度requests模組:http://www.baidu.com/s?wd=requests模組
  • ? POST請求引數是在請求體當中,訊息長度沒有限制且以隱式的方式進行傳送,通常用來向HTTP伺服器提交量比較大的資料(比如請求中包含許多引數或者檔案上傳操作等),請求的引數包含在“Content-Type”訊息頭裡,指明該訊息體的媒體型別和編碼

傳送GET請求

關鍵程式碼:requests.get(url)

引數說明:若需要傳請求引數,可直接在 url 後面新增,也可以在呼叫get()方法時通過關鍵字params傳入,需要注意的是params需要傳入dict (字典)型別。

​ 下面以請求百度為例,傳送get請求:

import requests

# 通過url直接加上請求引數,與通過params傳參效果是一樣的
response = requests.get(url='http://www.baidu.com/s?wd=requests模組')
# 通過params傳參
response2 = requests.get(url='http://www.baidu.com/s', params={"wd": "requests模組"})
print(response.status_code)		# 列印狀態碼
# print(response.text)		# 獲取響應內容

​ 執行結果:

C:\software\python\python.exe D:/learn/test.py
200

Process finished with exit code 0

傳送POST請求

關鍵程式碼:requests.post(url, data)

引數說明:可傳dict型別也可傳json型別,dict型別使用關鍵字data傳參,json型別則為使用關鍵字json傳參。若無需傳參可不傳。

register_url = "http://127.0.0.1:666/index/register"

# 新增請求頭,需要就傳
header = {
    "Content-Type": "application/json"
}

# json型別的引數
json = {
    "mobile_phone": "15612345678",
    "pwd": "Test1234",
    "type": 0
}
# 傳送post請求
response = requests.post(url=register_url, json=json, headers=header)
print(response.json())

# 列印結果:{'code': 200, 'msg': 'success', 'password': '321', 'username': '123'}

​ 上面舉例為json型別的傳參,json和dict型別的資料結構表面上看有點相似,那怎麼判斷什麼時候用json什麼時候用dict呢?只要檢視請求頭,需要哪種就傳哪種,表單型別的引數,即"Content-Type: application/x-www-form-urlencoded"就使用關鍵字data去傳dcit型別的引數,像上面舉例的"Content-Type": "application/json"json型別的引數一定要使用關鍵字json去傳遞。

獲取響應資料

​ 常見的屬性:

  • response.status_code:獲取響應狀態碼
  • response.cookies:獲取cookies
  • response.headers:獲取頭部資訊
  • response.url:獲取url
  • response.text:自動識別文字中的編碼格式進行解碼,但有時候不準確,會出現亂碼
  • response.content.decode('utf-8'):response.content,獲取位元組流的資料,進行decode解碼,預設是utf8
  • response.json():json方法可以將json字串轉換成對應的python型別的資料,介面返回的資料99%都是json型別的
import requests

response = requests.get(url='http://www.baidu.com/s?wd=requests模組')
# response = requests.get(url='http://www.baidu.com/s', params={"wd": "requests模組"})

print("這是status_code:{}\n".format(response.status_code))

print("這是cookies:.{}\n".format(response.cookies))

print("這是headers:.{}\n".format(response.headers))

print("這是url:.{}\n".format(response.url))

# print("這是響應頁面文字資訊:.{}\n".format(response.text))   # 因為返回資料太長,不作執行
# print("這是獲取的位元組流資料decode解碼:.{}\n".format(response.content.decode()))

​ 執行結果:

C:\software\python\python.exe D:/learn/test.py
這是status_code:200

這是cookies:.<RequestsCookieJar[<Cookie BAIDUID=6EC84DD4DE623D1500B3D0E771D1D3FA:FG=1 for .baidu.com/>, <Cookie BIDUPSID=6EC84DD4DE623D15C087081CA8B7A6D9 for .baidu.com/>, <Cookie H_PS_PSSID=32292_1441_32355_32328_31253_32348_32046_32394_32429_32115_32437 for .baidu.com/>, <Cookie PSINO=6 for .baidu.com/>, <Cookie PSTM=1596554477 for .baidu.com/>, <Cookie delPer=0 for .baidu.com/>, <Cookie BDSVRTM=11 for www.baidu.com/>, <Cookie BD_CK_SAM=1 for www.baidu.com/>]>

這是headers:.{'Bdpagetype': '3', 'Bdqid': '0xf2cd9ccf00070e08', 'Cache-Control': 'private', 'Ckpacknum': '2', 'Ckrndstr': 'f00070e08', 'Connection': 'keep-alive', 'Content-Encoding': 'gzip', 'Content-Type': 'text/html;charset=utf-8', 'Date': 'Tue, 04 Aug 2020 15:21:17 GMT', 'P3p': 'CP=" OTI DSP COR IVA OUR IND COM ", CP=" OTI DSP COR IVA OUR IND COM "', 'Server': 'BWS/1.1', 'Set-Cookie': 'BAIDUID=6EC84DD4DE623D15C087081CA8B7A6D9:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com, BIDUPSID=6EC84DD4DE623D15C087081CA8B7A6D9; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com, PSTM=1596554477; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com, BAIDUID=6EC84DD4DE623D1500B3D0E771D1D3FA:FG=1; max-age=31536000; expires=Wed, 04-Aug-21 15:21:17 GMT; domain=.baidu.com; path=/; version=1; comment=bd, delPer=0; path=/; domain=.baidu.com, BD_CK_SAM=1;path=/, PSINO=6; domain=.baidu.com; path=/, BDSVRTM=11; path=/, H_PS_PSSID=32292_1441_32355_32328_31253_32348_32046_32394_32429_32115_32437; path=/; domain=.baidu.com', 'Traceid': '1596554477018818305017495812540276870664', 'Vary': 'Accept-Encoding', 'X-Ua-Compatible': 'IE=Edge,chrome=1', 'Transfer-Encoding': 'chunked'}

這是url:.http://www.baidu.com/s?wd=requests%E6%A8%A1%E5%9D%97


Process finished with exit code 0

高階操作

​ 在上面我們基本瞭解了requests庫的基本用法,如GET、POST請求以及response的屬性,接下來我們再看下requests庫的一些高階用法,如下載/上傳檔案、Cookies設定、代理設定等

檔案下載

​ 與傳送請求區別無二,只是儲存時需要處理一下,如下載圖片,把獲取圖片的二進位制資料然後用二進位制寫入檔案即可。

import requests
 
response = requests.get('https://github.com/favicon.ico')
 
# 獲取二進位制資料,然後寫入檔案
with open('favicon.ico','wb')as f:
    f.write(response.content)
    f.close

檔案上傳

​ 關鍵程式碼:requests.post(utl, files=files)

import requests
 
file = {'file':open('favicon.ico','rb')}	# 將之前抓取的github圖示以二進位制格式讀取

response = requests.post('http://httpbin.org/post', files = file)

print(response.text)

SSL證照驗證

​ 為什麼會有SSL證照驗證,SSL是什麼?

​ SSL協議是網路層和傳輸層的協議。SSL(Secure Sockets Layer 安全套接層)協議,及其繼任者TLS(Transport Layer Security傳輸層安全)協議,是為網路通訊提供安全及資料完整性的一種安全協議。

​ HTTPS是相容HTTP的,可以把HTTPS理解為'HTTP over TSL',即HTTPS是HTTP協議和TSL協議的組合。HTTPS在傳輸資料時,同樣會先建立TCP連線,建立起TCP連線後,會建立TSL連線。請求可以為HTTPS請求驗證SSL證照,就像我們使用的瀏覽器一樣,SSL驗證預設是開啟的,如果證照驗證失敗,請求會丟擲一個SSLError,如下圖:

【python介面自動化】- 使用requests庫傳送http請求

​ 碰到請求SSL驗證的,我們是可以直接跳過不驗證的,通過設定verify=False就可關閉錯誤提示,跳過SSL驗證,這裡只是忽略了SSL驗證,並不是沒有了SSL驗證,它仍然會存在一個警告資訊InsecureRequestWarning。

import requests
#通過一下兩行程式碼即可把警報消除,即使verify=False,報警還是存在的
from requests.packages import urllib3
 
urllib3.disable_warnings()
 
response = requests.get('https://www.12306.cn',verify = False)
print(response.status_code)

保持會話

​ 在requests中,如果直接使用get()post()等方法是可以做到模擬網頁的介面請求,但是每次發起請求結束後它就結束了,並不會儲存相關的驗證資訊,如cookies/token;比如第一次使用post()請求登入了某個網站,第二次想獲取成功登入後的使用者個人資訊,再一次發起post()請求時它會要求你需求先登入,明明第一次請求時已經登入了,為什麼第二次還提示要先登入呢?實際上兩次請求相當於使用了兩個瀏覽器去訪問,是兩個完全不相關的會話,因此第二次請求是拿不到使用者資訊的。

​ requests中的session物件能夠讓我們跨http請求保持某些引數,即讓同一個session物件傳送的請求頭攜帶某個指定的引數。當然,最常見的應用是它可以讓cookie保持在後續的一串請求中。

# 建立一個session物件,使它能夠自動記錄上一次請求中的cookie資訊
se = requests.session()

# 登入介面
login_url = "http://www.test.com/api/member/login"
login_data = {
    "mobilephone": "15612345678",
    "pwd": "Test1234"
}
res = se.post(url=login_url, data=login_data)

# 登入後查詢使用者最近的訂單記錄
order_record_url = "http://www.test.com/api/member/order"
record_data = {
    "beginTime": "2020-08-01",
    "endTime": "2020-08-15"
}
res2 = se.post(url=order_record_url, data=record_data)
print(res2.json())

requests封裝

​ 一次封裝,一勞永逸,之後直接呼叫即可,封裝內容按需。

import requests


class SendSessionRequest:
    """使用session鑑權的介面,記錄cookies/token"""

    def __init__(self):
        self.session = requests.session()

    def requests(self, url, method, params=None, data=None, json=None, headers=None):
        method = method.lower()
        if method == "post":
            return self.session.post(url=url, json=json, data=data, headers=headers)
        elif method == "patch":
            return self.session.patch(url=url, json=json, data=data, headers=headers)
        elif method == "get":
            return self.session.get(url=url, params=params)


if __name__ == '__main__':
    login_url = "http://www.test.com/member/login"
    login_data = {"mobile_phone": "15612345678", "pwd": "Test1234"}
    header = {"Content-Type": "application/json"}
    se = SendSessionRequest()
    res = se.requests(url=login_url, method="post", json=login_data, headers=header)
    print(res.json())

相關文章