requests的深入刨析及封裝調用

前言

說到python發送HTTP請求進行接口自動化測試,腦子裏第一個閃過的可能就是requests庫了,當然python有很多模塊可以發送HTTP請求,包括原生的模塊http.client,urllib2等,但由於原生的模塊過於複雜,使用繁瑣,那麼requests庫就誕生了,它也是現階段比較流行的接口自動化測試工具之一

requests是個第三方庫,封裝了HTTP請求的所有方法,使用方便簡單,只需要根據不同的請求方式調用相對應的方法就可以完成發送網絡請求的整個過程,那麼今天我們就來說說requests庫是如何使用的,那麼在開始之前我們大致說一下今天的主要內容:

1. requets如何發送get請求

2. requests如何發送post請求

3. params參數和data參數的區別

4. requests庫中Session類的的妙用

5. 針對get請求和post請求,對requests進行簡單封裝

安裝

通常,我們使用第三方庫之前,都需要先進行安裝

cmd執行命令 pip install requests 接着就可以引入該模塊使用其提供的方法了

import requests

發送get請求

requests是通過調用get()方法來完成發送get請求的,那麼,在掌握requests庫如何發送get請求之前,你還應該簡單瞭解一下什麼是get請求,你可以參照這篇文章https://www.cnblogs.com/linuxchao/p/linuxchao-http.html

通常在我們學習一個方法如何使用之前,我們需先知道這個方法需要哪些參數?參數類型是什麼? 那麼我們就先分析一下get()方法的源碼

 1 def get(url, params=None, **kwargs):
 2     r"""Sends a GET request.
 3 
 4     :param url: URL for the new :class:`Request` object.
 5     :param params: (optional) Dictionary, list of tuples or bytes to send
 6         in the body of the :class:`Request`.
 7     :param \*\*kwargs: Optional arguments that ``request`` takes.
 8     :return: :class:`Response <Response>` object
 9     :rtype: requests.Response
10     """
11 
12     kwargs.setdefault('allow_redirects', True)
13     return request('get', url, params=params, **kwargs)

這就是get方法的源碼了,你應該能夠發現,get()方法只有一個必傳的參數url,其他參數都是非必傳的,那麼其他的參數有什麼作用呢?

params

對於這個參數,可以是字典,也可以是嵌套元組的列表,基於get請求的特點,請求參數是可以直接跟在URL之後的,以?號開始以key=value的形式傳遞(多個參數使用&符號進行分割),但是爲了明確區分URL和參數,就需要使用params參數傳遞

**kwargs:其他一些關鍵字參數,暫時不做介紹

接下來我們來看2個簡單的實例,體會一下reauets通過get()方法發送一個不帶參數的get請求和帶參數的請求的過程

通過get()方法發送get請求訪問博客園首頁

"""
------------------------------------
@Time : 2019/7/11 20:34
@Auth : linux超
@File : requests_blog.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import requests  # 導入requests模塊


response = requests.get('https://www.cnblogs.com/')  # 發送get請求
print(response.text)  # 獲取響應數據

響應數據

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="referrer" content="always" />
    <title>博客園 - 代碼改變世界</title>
        <meta name="keywords" content="開發者,博客園,開發者,程序猿,程序媛,極客,編程,代碼,開源,IT網站,Developer,Programmer,Coder,Geek,技術社區" />
            <meta name="description" content="博客園是一個面向開發者的知識分享社區。自創建以來,博客園一直致力並專注於爲開發者打造一個純淨的技術交流社區,推動並幫助開發者通過互聯網分享知識,從而讓更多開發者從中受益。博客園的使命是幫助開發者用代碼改變世界。" />
    <link rel="shortcut icon" href="//common.cnblogs.com/favicon.ico" type="image/x-icon" />
    <link rel="Stylesheet" type="text/css" href="/bundles/aggsite.css?v=IlEZk4Ic2eCzcO6r0s4bGm62FAo8VZI-US_0EqUe2Bk1" />
        <link id="RSSLink" title="RSS" type="application/rss+xml" rel="alternate" href="http://feed.cnblogs.com/blog/sitehome/rss" />
    <script src="//common.cnblogs.com/scripts/jquery-2.2.0.min.js" type="text/javascript"></script>
    <script src="/bundles/aggsite.js?v=cE0bqImLWsEG3gZXOupKxj5tj_ukK7pLeSd73DHZOT81" type="text/javascript"></script>
    <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script>
    <script>
        var googletag = googletag || {};
        googletag.cmd = googletag.cmd || [];
    </script>

這裏我只截取了一部分響應數據,響應數據其實是博客園的首頁HTML源碼

可以看到只需要一行代碼即可完成整個請求過程,通過response.text得到響應數據(其實這個過程和我們在瀏覽器中輸入博客園地址訪問是一樣的),當然你還可以使用以下的方法獲取不同的響應數據

response.headers  # 獲取響應頭信息
response.cookies  # 獲取返回的cookie
response.status_code  # 獲取狀態碼

發送帶params參數的get請求

"""
------------------------------------
@Time : 2019/7/11 20:34
@Auth : linux超
@File : requests_blog.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import requests

login_url = r'http://***/futureloan/mvc/api/member/login'  # 接口地址
login_data = {"mobilephone": "13691579841", "pwd": 123456}  # 接口所需參數
response = requests.get(url=login_url, params=login_data)  # 發送get請求
print(response.text)  # 獲取響應數據
print(response.url)

響應數據

{"status":1,"code":"10001","data":null,"msg":"登錄成功"}
http://***/futureloan/mvc/api/member/login?mobilephone=13691579841&pwd=123456
Process finished with exit code 0

我們通過傳遞一個字典給params參數,即可完成帶參數的get請求,並獲取響應數據

注意

1.我們日常工作中也最好傳遞字典作爲接口數據,如果數據類型是json則在發送請求的時候需要先轉成字典

2.只要你發送的是get請求,且不想通過url拼接的方式直接傳遞接口參數,那麼只能使用params參數來傳遞(如果你使用data,json等參數時你會發現,請求並不會成功),因爲通過params傳遞的參數會附加到url後,這也是get請求的特點,因此你需記住:發送get請求時參數只能使用params即可

以上只是簡單的發送get請求的方法,至於如何發送帶其他參數的get請求(比如headers,cookies等),還需對get()進一步的研究和實踐

發送post請求

同樣,在開始學習下面的內容之前,仍建議你先了解什麼是post請求及其特點,對你學習接下來的內容也會更好理解https://www.cnblogs.com/linuxchao/p/linuxchao-http.html

requests發送post請求是通過post()方法來實現的,那麼我們還是先看一下它的源碼

 1 def post(url, data=None, json=None, **kwargs):
 2     r"""Sends a POST request.
 3 
 4     :param url: URL for the new :class:`Request` object.
 5     :param data: (optional) Dictionary (will be form-encoded), bytes, or file-like object to send in the body of the :class:`Request`.
 6     :param json: (optional) json data to send in the body of the :class:`Request`.
 7     :param \*\*kwargs: Optional arguments that ``request`` takes.
 8     :return: :class:`Response <Response>` object
 9     :rtype: requests.Response
10     """
11 
12     return request('post', url, data=data, json=json, **kwargs)

通過源碼我們可以發現,post()和get()一樣,僅有一個url參數是必傳的,其他仍然可以不傳遞,但是其中data和json參數卻是post()方法中最重要的參數,下面就說一下何時使用data,何時使用json

data

大多數post請求的接口默認支持參數類型Content-Type爲application/x-www-form-urlencoded, 它告訴我們請求的接口參數需要傳遞一個form表單,那麼我們往往是通過構造一個字典來傳遞form表單的,

所以當我們向服務器提交form表單時就可以使用data參數,它會接收一個字典類型的數據,存放到請求體中,然後發送給服務器(參數需是字典類型)

json

首先你訪問的接口需要支持content_type爲application/json格式的數據類型,那麼你就可以通過json這個參數來傳遞接口參數(參數可以是字典也可以是json類型)

下面我們來看一個發送post請求,使用data參數的實例

"""
------------------------------------
@Time : 2019/7/12 10:22
@Auth : linux超
@File : requests_blog.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import requests

login_url = r'http://***/futureloan/mvc/api/member/login'  # 接口地址
login_data = {"mobilephone": "13691579841", "pwd": 123456} # 接口所需參數
response = requests.post(url=login_url, data=login_data)  # 發送post請求
print(response.text)  # 獲取響應數據
print(response.url)
print(response.status_code)

響應數據

{"status":1,"code":"10001","data":null,"msg":"登錄成功"}
http://***:8080/futureloan/mvc/api/member/login
200

Process finished with exit code 0

使用json參數實例

"""
------------------------------------
@Time : 2019/7/12 10:22
@Auth : linux超
@File : requests_blog.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import requests

login_url = r'http://***/futureloan/mvc/api/member/login'  # 接口地址
login_data = {"mobilephone": "13691579841", "pwd": 123456} # 接口所需參數
response = requests.post(url=login_url, json=login_data)  # 發送post請求
print(response.text)  # 獲取響應數據
print(response.url)
print(response.status_code)

響應數據

{"status":0,"code":"20103","data":null,"msg":"手機號不能爲空"}
http://***/futureloan/mvc/api/member/login
200

Process finished with exit code 0

可以看到使用data參數和json參數,獲取到的返回結果是不一樣的,因爲我這裏使用的接口不支持application/json數據格式,所以當你使用json參數傳遞參數時,服務器是無法解析數據的,也就不會返回正確的結果了

所以對於何時使用data參數,何時使用json參數,還需要根據實際的接口所支持的數據類型進行選擇

params和data區別

上面已經說過get請求中的params參數和post請求中的data參數,那麼這兩個參數到底有什麼區別呢?

1. 發送get請求時,由於get請求沒有請求體,請求參數只能跟在url地址後的,而且服務器也只能通過解析url獲得請求的參數,因此get()方法發送get請求時只能使用params參數,它會把請求的參數默認追加到url地址後面

2. 通常情況下用戶需要提交某些數據時,發送的請求一般都爲post請求,post請求會提交一個form表單,那麼我們就可以構造一個字典格式的數據,使用data參數傳遞,由於post請求是有請求體的,而且請求參數就存放在請求體中,服務器也只能通過解析請求體中內容而獲得請求的參數,所以post請求不能使用params傳遞接口參數,只能使用data,json,file等, data參數會把請求參數放到請求體中

Session類的妙用

實際工作中,我們會經常遇到需要保持某一個狀態,才能測試後續的接口,比如說:充值接口,那麼需要用戶先登錄,且一直保持登錄狀態才能進行充值,那麼對於這種情況該怎麼解決呢?這就要用到requests庫中的Session類了,Session可以保持請求的狀態,像我們訪問某個網站一樣,我們只要登錄後就可以瀏覽該網站上的任意頁面,先看下面實例

"""
------------------------------------
@Time : 2019/7/12 10:22
@Auth : linux超
@File : requests_blog.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import requests

login_url = r'http://***/futureloan/mvc/api/member/login'  #  登錄接口地址
login_data = {"mobilephone": "13691579841", "pwd": 123456} # 接口所需參數
response_login = requests.post(url=login_url, data=login_data)  # 發送post請求 登錄
print(response_login.text)
recharge_url = r'http://***/futureloan/mvc/api/member/recharge'  # 充值接口地址
recharge_data = {"mobilephone": "13691579841", "amount": 10000.00}  # 接口所需參數
response_recharge = requests.post(url=recharge_url, data=recharge_data)  # 發送請求,開始充值
print(response_recharge.text)

執行結果

{"status":1,"code":"10001","data":null,"msg":"登錄成功"}
{"status":0,"code":null,"data":null,"msg":"抱歉,請先登錄。"}

Process finished with exit code 0

可以發現,我們之前都已經登錄過了,但是充值時卻失敗了,原因就是直接使用reauests來發送請求時,並不會保持當前的狀態(這也是HTTP請求的缺陷),現在我們使用Session對像再次發送充值請求,修改代碼

"""
------------------------------------
@Time : 2019/7/12 10:22
@Auth : linux超
@File : requests_blog.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import requests


request = requests.Session()  # 初始化Session
login_url = r'http://***/futureloan/mvc/api/member/login'  #  登錄接口地址
login_data = {"mobilephone": "13691579841", "pwd": 123456} # 接口所需參數
response_login = request.request(method='post', url=login_url, data=login_data)  # 發送post請求 登錄
print(response_login.text)
recharge_url = r'http://***/futureloan/mvc/api/member/recharge'  # 充值接口地址
recharge_data = {"mobilephone": "13691579841", "amount": 10000.00}  # 接口所需參數
response_recharge = request.request(method='post', url=recharge_url, data=recharge_data)  # 發送請求,開始充值
print(response_recharge.text)

執行結果

{"status":1,"code":"10001","data":null,"msg":"登錄成功"}
{"status":1,"code":"10001","data":
{"id":5451,"regname":"test","pwd":"E10ADC3949BA59ABBE56E057F20F883E","mobilephone":"13691579841","leaveamount":"15000.00","type":"1","regtime":"2019-05-26 19:08:44.0"},

"msg":"充值成功"} Process finished with exit code 0

可以發現,我們改用Session對象來發送充值請求就成功了。那這是什麼原因呢?

簡單來說,當我們第一次請求服務器時,獲取的響應信息會包含一個set-cookie的字段,保存了我們登錄的cookies信息,如果我們想保持這個狀態,那麼再次訪問服務器時就需要帶上這個cookies傳遞給服務器,才能保持這個狀態。

那麼我們使用Session對象發送請求時,Session會自動幫我們完成上述的過程,Session會自動把cookies的信息傳遞給服務器,而無需我們在請求參數中手動添加cookies,這樣就保持了登錄的狀態,後續的依賴操作都可以正常執行了

reqests簡單封裝

有人會問,requests庫已經封裝的很好了,直接用就行了,爲啥還要自己封裝一次?

第一. 通過封裝,我可以直接把所有的請求參數統一使用字典來傳遞

比如,我們接口請求需要的數據也就是測試數據往往會保存在excel表裏面,那麼我們取到後是字符串類型,字符串是無法作爲請求參數傳遞的,所以我每次都要做數據轉換,再傳遞給接口,爲了節省這個過程,我只需要把這個過程封裝到我的requests裏即可,每次取數據後自動給我處理

第二. 當我想保持某個狀態時,不想每次都初始化一個Session對象,那麼我可以把它封裝到我的requests裏面,以後直接調用即可

下面來看封裝的代碼

 1 """
 2 ------------------------------------
 3 @Time : 2019/7/12 16:14
 4 @Auth : linux超
 5 @File : sendrequests.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 @QQ   : [email protected]
 9 @GROUP: 878565760
10 ------------------------------------
11 """
12 import json
13 import requests
14 
15 
16 class HttpRequests(object):
17     """
18     eg: request = HttpRequests()
19         response = request(method, url, data)
20         or
21         response = request.send_request(method, url, data)
22         print(response.text)
23     """
24     def __init__(self):
25         self.session = requests.Session()
26 
27     def send_request(self, method, url, params_type='form', data=None, **kwargs):
28         method = method.upper()
29         params_type = params_type.upper()
30         if isinstance(data, str):
31             try:
32                 data = json.loads(data)
33             except Exception:
34                     data = eval(data)
35         if 'GET' == method:
36             response = self.session.request(method=method, url=url, params=data, **kwargs)
37         elif 'POST' == method:
38             if params_type == 'FORM':  # 發送表單數據,使用data參數傳遞
39                 response = self.session.request(method=method, url=url, data=data, **kwargs)
40             elif params_type == 'JSON':  # 如果接口支持application/json類型,則使用json參數傳遞
41                 response = self.session.request(method=method, url=url, json=data, **kwargs)
42             else:  # 如果接口需要傳遞其他類型的數據比如 上傳文件,調用下面的請求方法
43                 response = self.session.request(method=method, url=url, **kwargs)
44         # 如果請求方式非 get 和post 會報錯,當然你也可以繼續添加其他的請求方法
45         else:
46             raise ValueError('request method "{}" error ! please check'.format(method))
47         return response
48 
49     def __call__(self, method, url, params_type='form', data=None, **kwargs):
50         return self.send_request(method, url,
51                                  params_type=params_type,
52                                  data=data,
53                                  **kwargs)
54 
55     def close_session(self):
56         self.session.close()
57         try:
58             del self.session.cookies['JSESSIONID']
59         except:
60             pass
61 
62 
63 request = HttpRequests()
64 
65 
66 if __name__ == '__main__':
67     pass

這個封裝只針對了get請求和post請求,當然你也可以把put,delete等請求添加在32行代碼後面,實現更多的請求方式

解釋一下30-34行代碼: 這幾行數據是爲了把json和字符串類型的數據轉換爲字典的格式(通過使用字典傳遞接口參數)且可以處理一些特殊的形式,比如下面這樣的格式

'{"mobilephone": None, "pwd": null}' # 字符串類型的,但是即不是json形式的字符串,也不是字典類型的字符串,因爲字典裏面沒有null

封裝測試

現在我們使用封裝好的方法來測試一下發送登錄和充值接口的請求

"""
------------------------------------
@Time : 2019/7/12 16:16
@Auth : linux超
@File : test_requests.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
from sendrequests import request
import unittest


class TestRequests(unittest.TestCase):

    # 登錄接口地址
    login_url = r'http://***/futureloan/mvc/api/member/login'
    # 登錄接口測試數據
    login_test_value = '{"mobilephone": "13691579841", "pwd": 123456}'

    # 充值接口地址
    recharge_url = r'http://***/futureloan/mvc/api/member/recharge'
    # 充值接口測試數據
    recharge_test_value = {"mobilephone": "13691579841", "amount": 10000.00}

    def test_login_api(self):
        """登錄接口測試用例"""
        response = request('get', url=self.login_url, data=self.login_test_value)
        self.assertTrue(response.json()["code"] == "10001")
        print("登錄接口測試通過")

    def test_recharge_api(self):
        """充值接口測試用例"""
        response = request('get', url=self.login_url, data=self.login_test_value)
        try:
            # 充值接口需要先登錄,才能充值
            self.assertTrue(response.json()["code"] == '10001')
        except AssertionError as e:
            print('登錄失敗!')
            raise e
        else:
            response = request('post', url=self.recharge_url, data=self.recharge_test_value)
            self.assertTrue(response.json()["code"] == "10001")
            print("充值接口測試通過")

if __name__ == '__main__':
    unittest.main()

測試結果

登錄接口測試通過
..
充值接口測試通過
----------------------------------------------------------------------
Ran 2 tests in 0.570s

OK

Process finished with exit code 0

ok,測試代碼執行通過,說明我們的封裝沒有啥問題, 且可以正常發送get和post請求,也可以解決測試數據問題和需要接口依賴的問題

總結

最後我們再來總結一下本文涉及到的所有的知識點和你需要掌握的

1. requests發送get請求和post請求的方法

get(url, params=None, **kwargs)
post(url, data=None, json=None, **kwargs)

2. parmas參數和data參數的區別

  由於get請求無請求體,post請求有請求體

  使用params參數時,默認會把參數附加到url後面,所以發送get請求時應使用params參數

  使用data參數時,參數會存放到請求體中,所以發送post請求時不能使用params,應使用data,除非接口及支持get又支持post,同樣get請求也不能使用data參數

3. 如何使用Seesion解決接口保持狀態的問題

  初始化Session實例,通過這個實例調用request()方法發送請求

4. 最重要的一個封裝方法,並掌握這個封裝該如何使用

  主要針對get和post請求的接口

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章