1. 程式人生 > >第5章 》模擬網絡請求(一)

第5章 》模擬網絡請求(一)

加密 ati sts 選擇 就會 main ... http請求 還需要

  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章 》模擬網絡請求(一)