前言
selenium是一個web自動化測試的開源框架,它支持多語言:python/java/c#…
前面也有一篇文章說明了,selenium+瀏覽器的環境搭建。
selenium支持多語言,是因為selenium與瀏覽器驅動之間是通過http協議進行通信的。只關心通信的數據是否能夠正確解讀 ,並不關心這個數據是從哪個客戶端來。無論來自python\java,還是jmeter,postman都沒有問題。
本篇文章中,以requests做為客戶端,跳過selenium,直接與谷歌瀏覽器驅動(chromedriver)進行http通信,驅動chrome瀏覽器去執行命令。
requests庫
先解釋一下requests庫:一個python的第三方庫,是目前最好用的http請求庫。
直接封裝了get請求、post請求。
只需要提供 請求url、請求方法、請求內容即可。
以下為request庫使用的簡單示例(request的詳細使用可參看其它博主其它的博文):
import requests s = requests.session() response = s.get("http://www.baidu.com") # 發起get請求 print(response.text) # 獲取響應結果的 響應數據 res = response.json() # 將 響應數據 轉換成python數據對象。
如果我要利用requests庫,去向chromedriver發送請求。那么我必須得了解請求的類型、請求的數據、請求的內容是什么。
基於此,我們需要了解在selenium庫當中,會有哪些請求?
需要解決的問題
1、selenium有哪些請求?
2、每一個請求的請求url、請求類型如何獲取?
3、每一個請求的請求數據又如何獲取?
selenium - json wire protocol - 獲取請求url和類型
要想解決以上3個問題,我們需要了解selenium的部分原理。
在selenium與驅動進行http通信的協議全稱叫做:json wire protocol.
我們在使用selenium庫驅動瀏覽器的時候,我們的操作有一部分大概是以下這樣的:
1)打開chrome瀏覽器;
2)訪問某一個網址;
3)查找該網址中的某一個元素;
4)操作3)中查找到的元素。
在selenium庫看來,以上每一步操作都是一個http請求,也叫做命令(Command)。
chromedriver在收到這個請求之后,再去驅動對chrome瀏覽器執行對應的動作。
所以,在selenium庫當中,存儲了所有命令(Command)名稱、命令對應的http請求類型、命令對應的請求url。
首先,來看看Command的名稱(選取幾個大家熟知的操作):
訪問網站命令(GET)對應的請求類型和請求url為:
從上圖可以看出,GET命令是post請求,請求地址只有一部分。
url中有3個問題:
1)請求的url並不完整。
url中,缺失中base地址。base地址為,chromedriver的ip+端口號。因為,命令是發給chromedriver去執行的。
2)url當中的$sessionId是什么?
在selenium當中,每開啟一次與chromedriver的會話,都會生成一個會話ID。sessionId就是這個會話ID。在很多的命令請求當中,在請求地址中,通過sessionId都綁定了當前的會話。
換句話說,我們要用requests與chromedriver進行通信,那么我們首先,得生成會話ID,並得到這個ID值,才能夠進一步的去訪問網頁,去發送更多的瀏覽器操作命令。
3)sessionId從何而來?如何獲取?
在selenium當中,通過NEW_SESSION請求來開啟會話,chromedriver在收到請求后,在響應數據中,返回本次會話的sessionId
請求的參數如下(啟動什么類型的瀏覽器、有什么配置參數):
1 params = {'capabilities': { 2 'firstMatch': [{}], 3 'alwaysMatch': {'browserName': 'chrome', 4 'platformName': 'any', 5 'goog:chromeOptions': {'extensions': [], 'args': []} 6 }}, 7 'desiredCapabilities': {'browserName': 'chrome', 8 'version': '', 9 'platform': 'ANY', 10 'goog:chromeOptions': {'extensions': [], 'args': []}} 11 }
chromedriver在正常收到請求之后,響應的數據如下(主要為sessionId):
{ "sessionId": "ed76b48661b6fe58b9be6f56716531b7", # 本次會話的sessionId "status": 0, "value": { "acceptInsecureCerts": false, "acceptSslCerts": false, "applicationCacheEnabled": false, "browserConnectionEnabled": false, "browserName": "chrome", "chrome": { "chromedriverVersion": "74.0.3729.6 (255758eccf3d244491b8a1317aa76e1ce10d57e9-refs/branch-heads/3729@{#29})", "userDataDir": "/var/folders/gm/k4pj0kf50vz9f3gznsp4cn340000gn/T/.com.google.Chrome.OPZURo" }, "cssSelectorsEnabled": true, "databaseEnabled": false, "goog:chromeOptions": { "debuggerAddress": "localhost:63649" }, "handlesAlerts": true, "hasTouchScreen": false, "javascriptEnabled": true, "locationContextEnabled": true, "mobileEmulationEnabled": false, "nativeEvents": true, "networkConnectionEnabled": false, "pageLoadStrategy": "normal", "platform": "Mac OS X", "proxy": {}, "rotatable": false, "setWindowRect": true, "strictFileInteractability": false, "takesHeapSnapshot": true, "takesScreenshot": true, "timeouts": { "implicit": 0, "pageLoad": 300000, "script": 30000 }, "unexpectedAlertBehaviour": "ignore", "version": "75.0.3770.100", "webStorageEnabled": true } }
ps: 可訪問此網站了解詳情:https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
selenium - 每一個命令函數 - 設置請求數據
以上我們獲取到了每一個命令的請求地址和請求類型。那么請求數據從哪里獲取 呢?
每一個命令的功能不一樣,請求的數據也就不一樣。在selenium當中,都是在命令對應的函數當中去設置請求數據的。
比如,訪問網址操作命令,在selenium當中是get函數,那么我們去看get函數的源碼:
在上圖中的execute函數當中,第二個參數params對應的就是請求數據。所以get命令的請求體為:{"url":調用get函數傳進來的url值}
再比如,查找元素命令,在selenium當中是find_element函數,那么我們去看find_element的源碼:
所以find_elment命令的的請求體為:{"using":定位類型,"value":定位表達式}
利用requests - 開啟瀏覽器會話、訪問百度首頁、搜索檸檬班
1、啟動本地電腦 上的chromedriver程序。雙擊即可。默認的服務端口為9515
2、通過requests庫向chromedriver發起會話、並打開百度首頁的代碼如下:
1 #!/usr/bin/python3 2 # -*- coding: utf-8 -*- 3 # Name: use_request_send_http_chromedriver 4 # Author: liyuan 5 # Time: 15:52 6 7 # 1、base_url從哪里來的?如何確定? 8 # 2、命令的請求類型從哪里來的?請求地址從哪里來的? 9 # 3、請求參數從何獲取。 10 11 import requests 12 13 # chromedriver服務的base地址。 14 base_url = "http://127.0.0.1:9515" 15 # 創建會話 16 s = requests.Session() 17 18 19 # **************************向chromedriver發送的命令1:建立會話************************** 20 21 # 創建與chromedriver會話的請求數據 22 params = {'capabilities': { 23 'firstMatch': [{}], 24 'alwaysMatch': {'browserName': 'chrome', 25 'platformName': 'any', 26 'goog:chromeOptions': {'extensions': [], 'args': []} 27 }}, 28 'desiredCapabilities': {'browserName': 'chrome', 29 'version': '', 30 'platform': 'ANY', 31 'goog:chromeOptions': {'extensions': [], 'args': []}} 32 } 33 34 # 創建與chromedriver的會話。注意請求數據格式是application/json 35 res = s.request("POST",base_url+'/session',json=params) 36 # 獲取sessionId值 37 sessionid = res.json()["sessionId"] 38 39 40 # **************************向chromedriver發送的命令2:打開網址************************** 41 42 # 請求數據 43 datas = {'url': "http://www.baidu.com"} 44 # 請求地址拼接 45 url = base_url + "/session/{}/url".format(sessionid) 46 # 發送打開百度首頁的請求,注意請求數據格式是application/json 47 res = s.request("post",url,json=datas) 48 49 50 # # **************************向chromedriver發送的命令3:找到搜索輸入框************************** 51 # 請求數據 52 datas3 = {'using':"id","value":"kw"} 53 # 請求地址拼接 54 url3 = base_url + "/session/{}/element".format(sessionid) 55 # 發送打開百度首頁的請求 56 res3 = s.request("post",url3,json=datas3) 57 # 返回結果類似:{"sessionId":"2dae661546b28cd481d84048310fb196","status":0,"value"{"ELEMENT":"0.899392980463724-1"}} 58 # 獲取元素的id 59 ele_id = res3.json()["value"]["ELEMENT"] 60 61 62 # *****************向chromedriver發送的命令4:在搜索框當中輸入 檸檬班****************** 63 # /session/$sessionId/element/$id/value 64 # 請求數據 65 datas4 = {'text': '檸檬班', 'value': ['檸', '檬', '班']} 66 # 請求地址拼接 67 url4 = base_url + "/session/{}/element/{}/value".format(sessionid,ele_id) 68 # 發送打開百度首頁的請求 69 res4 = s.request("post",url4,json=datas4)
以上代碼運行的結果如下: