第5章 》模擬網絡請求(一)
python發送網絡請求是通過模塊中的方法來實現的,模塊定義好了請求的語法格式,只需要按照相應的語法格式對號入座寫代碼即可。
一般來說我們測試的都是基於HTTP和HTTPS的網絡請求,python有很多自動帶的原生模塊和擴展模塊均可以模擬網絡請求。當然我們在實際工作中會遇到很多其他協議請求,如WebSocket等。
本章以HTTP協議為例,采用原生模塊和擴展模塊對比的方式,給大家剖析一下原理。原理性的東西雖然廣泛不被重視,但它對大家之後的學習是十分有好處的。
5.1 發送HTTP請求
5.1.1 requests模塊
python有很多模塊都可以發送HTTP請求,包括原生的模塊http.client,urlib2等,第三方模塊requests等,都封裝了發送HTTP請求的方法。由於原生的模塊過於復雜,不推薦使用,之後所有的請求都是基於第三方模塊requests進行的,該模塊的好處在於簡單,把請求的框架都搭建好了,只需要填入相應的參數數據,就能發送網絡請求了。
requests模塊非原生模塊,所有要使用還需要先進行安裝,按照之前安裝模塊的方法,打開命令行(在運行中輸入cmd),然後在命令行輸入:
pip install requests
接前就會自動安裝requests模塊及其相關模塊,然後就可以引入該模塊使用其提供的方法了。
import requests
後面會通過一些實例來介紹requests的具體使用方法。
5.1.2 請求與響應
HTTP就是發送請求和獲取響應的一個過程,而requests模塊只需要一步就能完成這樣的一個過程,並且request支持所有的HTTP請求的方法和響應數據,先進行語法介紹:
r = requests.方法(url, headers, data, ...)
其中url參數為必填的,畢竟HTTP請求就是對指定的URL進行發送,其他各種參數可根據實際請求的需要選擇性使用。
發送請求後會獲取響應結果,然後把結果賦值給變量,最後通過變量的屬性值取出需要的結果,下面就是常用返回結果:
r.headers 獲取返回的頭信息
r.text 獲取返回的主體(其實就是網頁html)
r.cookies 獲取返回的cookies
r.status_code 獲取返回的狀態碼(通常會通過狀態碼判斷請求是否成功)
學會requests的請求方法和獲取響應的結果,就可以開始網絡請求的測試了,下面就通過代碼實例來熟悉requests的模塊吧。
5.1.3 請求參數
1.URL參數
URL是唯一的必填參數,既然是網絡請求,必須要有URL地址才能發送,就像快遞一定要寫目的地才能發件一樣。先從最簡單的HTTP請求講起,請求一個靜態頁面,比如訪問網易首頁,通過抓包可以看到該請求是用的get方式發送的,所有要調用requests的get()方法來發送請求。
以網易首頁作為請求例子,實例代碼:
import requests # 導入requests模塊;
test_url = "http://www.163.com" # 定義url
response = requests.get(test_rul) # 實例化requests.get()方法
print(response.status_code) # 輸出返回的狀態碼
print(response.headers) # 輸出返回的頭信息
print(response.text) # 輸出返回的主體
代碼說明:
1 導入requests模塊;
2 將網易首頁的URL賦值到變量test_url中(這樣的好處在於看起來清晰,也方便代碼今後的維護);
3 在get()方法中將變量test_url直接傳入,即完成了帶URL的get請求(結果會賦值到變量reponse中,reponse中包含了返回結果的所有數據,可以根據需要獲取想要的數據);
4、5、6 獲取並打印返回的結果(狀態碼、頭信息、主體)
運行結果如下圖:
可以看出通過requests發送的HTTP請求只有3行代碼,就那麽簡單的一行代碼完成整個請求。
整個請求過程全被封裝在方法之中,對於沒有編碼基礎的測試人員來說再適合不過了。
2、headers 參數
headers是最常用的參數之一,前面那個例子只是最簡單的請求,沒有帶其他參數信息,而很多時候需要帶入headers發送請求,才可以獲取相應的請求結果。
還是以網易首頁作為例子,如果是pc端的請求,會返回pc端的頁,如果是手機端的請求,則返回手機端的頁面,這時候就要帶上headers參數的請求,通過瀏覽器的抓包工具可以看到headers有個字段“User-Agent",而服務器就是根據這個字段來判斷訪問的來源,如果需要模擬手機端請求,需要將“User-Agent"改為請求的手機型號。
實例代碼:
import requests
test_url = "http://www.163.com"
h = {"User-Agent":"Android/H60-L01/4.4.2"} # 將headers賦值到變量h中;
reponse = requests.get(test_url, headers=h) # 在get()方法中加一個headers參數,然後將變量賦值給headers參數。
print(reponse.status_code)
print(reponse.headers)
print(reponse.text)
運行結果如下:
3、cookies 參數
cookies也是最常用的參數之一,因為只要涉及登錄後數據獲取,都需要用到cookies參數,那麽如何獲取到cookies呢,一種是通過post發送登錄請求,獲取返回值的cookies屬性(這個會在後面的實例中講解),還有一種就是通過瀏覽器的網絡抓包方式獲取,在還不會熟練使用request模塊的前提下,就先介紹如何通過抓包獲取cookies。
首先打開chrome瀏覽器,然後登錄網易郵箱(或其它網站),接著按F12(或在網頁右鍵,選擇檢查)打開開發者工具,抓取cookies的界面如下圖:
選擇application中的cookies,點擊對應的測試URL域名,再找到session屬性並且domain是對應測試請求的域名,然後這條信息name和value對應的就是cookies了。
cookies參數以字典的形式發送,只需要對應的將name和value傳入即可。
實例代碼:
import requests
c = {"JSESSIONID":"abcRPAFmBY_sqf2qRMmxw"}
test_url = "https://www.163.com/"
reponse = requests.get(test_url, cookies=c)
print(reponse.status_code)
print(reponse.headers)
print(reponse.text)
代碼說明:
2 將瀏覽器抓取的cookies信息以字典的形式賦值到變量c之中;
4 在get方法中加一個cookies參數,然後將變量c賦值給cookies參數,即完成了帶著cookies信息的get請求。
其實cookies也是可以通過headers參數傳遞的,只是不同之處在於cookies是以字典的形式發送的,而在headers之中cookies只是其中一個鍵,所以需要把cookies放到該鍵對應的值裏面,而對應的值是以key=value的形式傳入的,改一下代碼。
import requests
c = {"JSESSIONID":"abcRPAFmBY_sqf2qRMmxw"}
test_url = "https://www.163.com/"
reponse = requests.get(test_url, headers={"cookies":c}) # 此處cookies是作為headers的鍵值傳入;
print(reponse.status_code)
print(reponse.headers)
print(reponse.text)
代碼說明:
4 在get方法中將變量c賦值給headers參數中的cookies字段,即完成了帶著cookies信息的get請求。
以上的兩種方法,推薦使用cookies參數,一方面把cookies單獨分離出來,不用與其他headers的字段放在一起,讓代碼更清晰,另一方面通過post請求返回的cookies是可以直接賦值到cookies參數之中,不需要再做轉換。
4、params 參數
對於params參數可以存放請求的表單,並會以key1=value1&key2=value2&key3=value3的形式跟進URL之後發送,為了區分URL和參數,最好不要把表單放在URL之中,可以通過params參數進行發送, 上面網易郵箱的URL也是帶著參數的,直接放在URL之中,如果使用params參數就可以把後面的參數和URL分離。
還是以博客園登錄界面為例,只是URL部分把參數進行分離,代碼如下:
import requests
# test_url = "https://passport.cnblogs.com/user/signin?ReturnUrl=https%3A%2F%2Fwww.cnblogs.com%2F"
test_url = "https://passport.cnblogs.com/user/signin"
p = {"ReturnUrl":"https%3A%2F%2Fwww.cnblogs.com%2F"}
reponse = requests.get(test_url, params=p)
print(reponse.status_code)
print(reponse.headers)
print(reponse.text)
代碼說明:
4 將需要發送表單以字典的形式賦值到變量p之中。
5 在get方法中加一個params的參數,然後將變量p賦值給params參數,即完成了帶著params信息的get請求。
如果不使用params參數,也可以直接把表單加在URL中,只是代碼不清晰,所以不推薦直接把表單加在URL中。
5、data 參數
data參數也是用於存放請求表單,是request模塊中最重要的參數之一。
在使用data之前,先來了解一下post提交數據類型,區別與params,params只有一種類型(字符串類型),而post可以提交4種類型的數據,至於需要提交什麽類型的數據,取決於服務器接收的數據類型。post的數據類型需要和服務器接收的一致,不然服務器就無法正常識別post的數據,導致測試結果報錯。就像協議一樣,接收方預定接收哪種類型的表單,然後發頭方按照接收方指定的協議發送表單,這樣就完成了一個表單的提交。
如何識別服務器接收的數據類型?最簡單的方式還是抓包。抓取數據類型的界面如下:
通過抓包可以看到,在request headers 中有一個content-type的字段,這個字段表示了post發送數據的類型,一般分為以下4種類型:
第1種:content-type:application/json
實際上,現在越來越多的人反application/json作為請求頭,用來告訴服務器消息主體是序列化後的json字符串。由於json規範流行,除了低版本IE之外的各大瀏覽器都原生支持json.stringify,服務器端語言也都有處理json的函數,並且json格式支持比鍵值對更加復雜的結果化數據。
第2種:content-type:application/x-www-form-urlencoded
這是最常見的post提交數據的方式,瀏覽器的原生form表單,如果不設置enctype屬性,那麽最終就會以application/x-www-form-urlencoded方式提交數據。提交的數據按照key1=value1&key2=value2的方式進行編碼,key和value都進行了URL轉碼,然後打包發送到服務器。
第3種:content-type:multipart/form-data
content-type為multipart/form-data方式,主要用於上傳文件。需要註意的是同時form的enctype屬性也要設置為multipart/form-data,才能正確提交並解析所傳輸的數據。
第4種:content-type:text/xml
它是一種使用http作為傳輸協議,XML作為編碼方式的遠程調用規範。考慮到XML結構還是過於臃腫,一般場景用json會更靈活方便,所以這種提交我們的工作中實際使用的不多,僅了解一下就可以了。
最常見的是 第2種 類型,直接將數據以{key:value}的字典形式賦值給表單,然後通過request.post()中的data參數傳遞就可以了。
以網易音樂為例,搜索一首歌曲,通過抓包可以看到content-type是application/x-www-form-urlencoded,網易音樂的數據類型抓取圖如下:
搜索一首”一次就好“,通過抓包會發現有進行加密,那就直接把加密後的內容放到form之中發送出去,網易音樂的表單抓取如下圖:
實例代碼:
import requests
test_url = "https://music.163.com/weapi/search/suggest/web?csrf_token="
f = {"params":"ShQVLipkE8Y/p89iMoKOX5whEin1ZyoKeXZJJe+rBg+8mrEQT3RaAR5UP5B+ayoKrPIVzxsVuuFA1askCPcBzRJ6qOoPsPz06xStotZZGTmHrzaz1RcbiRpYOOrZl8NC",\
"encSecKey":"9ac26cbd1a87fd9790fb1cc2f0a475a5cd24be6b73e1b516f86b00ab58b568c0d4303eb6314bbedf5adedb968f047f44bb6ebe1fd9cfa09339a781d762e8aba0184c8a57dc53d1717f89d5c85d02e635cec9e7610fac4faac33838a3f299a1aa672390e4430b21ee2ea03e37aee53d17d70973933ffdf3fc7872101a9ff973da"\
}
reponse = requests.post(test_url, data=f)
print(reponse.status_code)
print(reponse.headers)
print(reponse.text)
代碼說明:
3 將需要發送的表單以字典形式賦值到變量form之中。
4 在post方法中加一個data參數,然後將變量賦值給data參數,即完成了帶著表單信息的post請求。
運行結果如下圖:
以上內容來自《python測試之道》——楊燕林、朱對洲、石赟 編著,若有侵權請聯系刪除。
第5章 》模擬網絡請求(一)