使用python實(shí)現(xiàn)一個(gè)瀏覽器自動(dòng)化的腳本
背景
最近工作中有這樣一個(gè)需求:客戶反饋在瀏覽器操作過(guò)程中,重復(fù)流程操作太頻繁,能不能讓瀏覽器自動(dòng)操作完成?
在我的認(rèn)知中,瀏覽器就是一個(gè)用來(lái)瀏覽頁(yè)面的工具,因此第一反應(yīng)就是“不可能!絕對(duì)不可能!”。領(lǐng)導(dǎo)說(shuō),你了解過(guò)RPA嗎?會(huì)后專門(mén)查閱了相關(guān)資料,在我的理解中,RPA就是一個(gè)定制化機(jī)器人(腳本流程),通過(guò)控制腳本操作顯示器,來(lái)模擬用戶操作的過(guò)程。
開(kāi)始
一開(kāi)始的時(shí)候,我在github上面找了一些開(kāi)源的項(xiàng)目,例如:PyRPA、RPA-Python、TagUI、openrpa等,但是因?yàn)槎ㄖ苹容^強(qiáng),后期不知道會(huì)變成怎么樣,因此做的要靈活一些。但是在這些開(kāi)源項(xiàng)目中,提供了一部分的api,但是在有些地方并不是很容易操作,感覺(jué)后面可能會(huì)不可控。
但是在我觀察中發(fā)現(xiàn),大多數(shù)的實(shí)現(xiàn)都是通過(guò)python
實(shí)現(xiàn)的,作為一個(gè)前端,第一反應(yīng)就是,為什么不用node和js結(jié)合呢,最后反應(yīng)過(guò)來(lái),使用python可以操作整個(gè)PC,但是如果你使用node或js,只能說(shuō)操作瀏覽器更方便一些了(其實(shí)和python差不多),只是python在使用第三方庫(kù)時(shí)候,會(huì)存在一定程度的延時(shí)。
因此,我決定放棄現(xiàn)有的RPA的項(xiàng)目,自己實(shí)現(xiàn)一個(gè)自動(dòng)化的python腳本。
語(yǔ)言選擇
關(guān)于我為什么選擇的python作為語(yǔ)言,其實(shí)在前面已經(jīng)說(shuō)過(guò)了,作為一個(gè)前端開(kāi)發(fā),其實(shí)最熟悉的莫過(guò)于javascript,另加一些nodejs,那么為什么我沒(méi)有用nodejs呢?node在前端開(kāi)發(fā)過(guò)程中,常常扮演著一個(gè)環(huán)境的角色,雖然也可以作為一個(gè)后端語(yǔ)言來(lái)寫(xiě)一些接口或服務(wù)(依賴于第三方庫(kù)),但是很少用到DOM的操作,操作瀏覽器的DOM,我們通常來(lái)說(shuō),都是使用js直接操作,或使用jquery等第三方工具。如果要再直接去攔截系統(tǒng)的一些操作,js就不太行了。
因此,采取了python,這一門(mén)熱度一直很高的語(yǔ)言。主要思路就是,圖片的對(duì)比,獲取點(diǎn)位,模擬用戶鼠標(biāo),鍵盤(pán)等操作。
瀏覽器的啟動(dòng)
其實(shí)這個(gè)啟動(dòng)瀏覽器,一開(kāi)始我只是單純的打開(kāi)瀏覽器,但是后面發(fā)現(xiàn),每天第一次啟動(dòng)的時(shí)候,都會(huì)特別慢,查閱資料也沒(méi)有找到原因,好像是緩存被清空了吧,網(wǎng)上找了大量的相關(guān)資料,最終找到了一個(gè)方案:加載時(shí)候啟動(dòng)指定驅(qū)動(dòng)程序(瀏覽器版本
與驅(qū)動(dòng)程序
需要對(duì)應(yīng)),指定版本瀏覽器下載,新版驅(qū)動(dòng)下載,114版本前驅(qū)動(dòng)下載,有一部分頁(yè)面需要科學(xué)上網(wǎng)才能訪問(wèn)與下載,建議大家科學(xué)上網(wǎng)昂。
一個(gè)啟動(dòng)瀏覽器的demo,這里的driver我在后續(xù)就不就行重復(fù)的定義了,直接使用了,例如:
from selenium import webdriver from selenium.webdriver.chrome.service import Service driver = webdriver.Chrome(service=Service(r"C:\\tb2Lot\\driver\\chromedriver.exe")) driver.maximize_window() driver.execute_script("document.charset='utf-8';") driver.get("http://www.baidu.com")
這時(shí)候我們就已經(jīng)可以打開(kāi)瀏覽器,并操作瀏覽器調(diào)取到百度的頁(yè)面了,我們想在百度的頁(yè)面輸入并搜索指定內(nèi)容怎么辦呢?這時(shí)候我們就需要獲取頁(yè)面的元素節(jié)點(diǎn),然后進(jìn)行輸入了,下面我們來(lái)看一下如何在瀏覽器搜索吧!
在百度進(jìn)行搜索的方案
首先我們先列舉一下目前能夠想到的兩個(gè)方案:
- 從頁(yè)面抓取頁(yè)面
元素路徑
,然后直接點(diǎn)擊元素; - 整個(gè)頁(yè)面截圖,再截取需要點(diǎn)擊的小圖片,獲取需要點(diǎn)擊的
圖片的坐標(biāo)
,點(diǎn)擊坐標(biāo);
這兩種方案對(duì)于簡(jiǎn)單的操作都是可行的,復(fù)雜一些的,會(huì)出現(xiàn)一些問(wèn)題,我們先看一下兩種方案分別怎么進(jìn)行操作,然后再來(lái)分析一下會(huì)遇見(jiàn)怎樣的問(wèn)題吧!
頁(yè)面抓取元素,直接點(diǎn)擊:
import pyautogui import pyperclip import time from selenium.webdriver.common.by import By def cvEnter(msg): pyperclip.copy(msg) time.sleep(1) pyautogui.hotkey('ctrl','v') time.sleep(1) pyautogui.hotkey('enter') #回車搜索 #driver的定義在本文最上面,這里沒(méi)有重復(fù)寫(xiě) #... cvEnter("稀土掘金") driver.find_element(By.XPATH,r'//*[@id="su"]').click() #通過(guò)瀏覽器的xpath觸發(fā)點(diǎn)擊事件 time.sleep(2) driver.find_element(By.XPATH,r'//*[@id="kw"]').clear() #清除輸入框 driver.find_element(By.XPATH,r'//*[@id="kw"]').send_keys('稀土掘金官網(wǎng)') #輸入框輸入數(shù)據(jù) pyautogui.hotkey('enter') time.sleep(5)
在上面,其實(shí)我們已經(jīng)使用了兩種輸入與搜索的方式:
輸入
- 方式一:通過(guò)
粘貼板
,直接進(jìn)行“c+v”; - 方式二:借助python瀏覽器的driver的
send_keys
方法,直接鍵入;
搜索(這里是回車和點(diǎn)擊實(shí)現(xiàn)的都是搜索)
- 方式一:使用pyautogui的
鍵盤(pán)熱鍵觸發(fā)
; - 方式二:借助python瀏覽器的driver獲取瀏覽器
元素
,觸發(fā)該元素的click
事件;
通過(guò)圖片的對(duì)比,獲取點(diǎn)位進(jìn)行點(diǎn)擊
import pyautogui import cv2 def getPosiXY(img): pyautogui.screenshot('./pics/screen.png') #顯示完成之后,截圖 screen = cv2.imread('./pics/screen.png') #加載全屏截圖 current = cv2.imread(f'./tb2lot_pic/{img}') #加載比對(duì)的圖片 result = cv2.matchTemplate(screen, current, cv2.TM_CCOEFF_NORMED) #在screen中尋找current的點(diǎn)位 pos_start = cv2.minMaxLoc(result)[3] x = int(pos_start[0]) + int(current.shape[1]/2) y = int(pos_start[1]) + int(current.shape[0]/2) return x,y pointX, pointY = getPosiXY("query_btn.png") pyautogui.click(pointX, pointY)
在上面的demo中,我們借助cv2
庫(kù),實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的大圖中尋找小圖的功能,這里在獲取之前自動(dòng)截圖
并保存
到指定目錄,小圖
的名稱作為參數(shù),傳遞到封裝的方法中,抓取點(diǎn)位信息,再點(diǎn)擊獲取到的坐標(biāo)
位置。因?yàn)槲覀冏ト〉淖鴺?biāo)點(diǎn),因此如果點(diǎn)擊的輸入框,輸入框粘貼數(shù)據(jù),就需要直接通過(guò)粘貼板進(jìn)行粘貼了。
為什么我們前面說(shuō)抓取元素會(huì)有問(wèn)題呢?
首先我們最理想的元素抓取就是,都在一個(gè)html中,沒(méi)有動(dòng)態(tài)的元素,沒(méi)有切換界面,沒(méi)有切換彈窗等,但是在實(shí)際的應(yīng)用過(guò)程中,往往都是比較復(fù)雜的,例如:
vue打包的html
(元素的id不是固定的,有時(shí)候元素節(jié)點(diǎn)渲染的問(wèn)題,嵌套可能有點(diǎn)區(qū)別);html嵌套了iframe
(直接切換了代碼引用的窗口,后面都統(tǒng)一叫做句柄);選擇框
的元素,展開(kāi)和關(guān)閉不一致;- ...
以上的問(wèn)題,都會(huì)導(dǎo)致直接抓取元素xpath
直接調(diào)取時(shí)候報(bào)錯(cuò)
,難道我們就真的不能用xpath進(jìn)行操作了嗎?答案當(dāng)然是否定的,但是也不是完全,例如在iframe
中,我們可以切換句柄
,來(lái)達(dá)到代碼引用模塊的更換,例如:
#driver的定義在本文最上面,這里沒(méi)有重復(fù)寫(xiě) #... iframeSrc = driver.find_element(By.XPATH,r'/html/body/div/iframe') driver.switch_to.frame(iframeSrc) # 切換句柄到iframe #driver.switch_to.default_content() #切換到默認(rèn)的句柄 driver.find_element(By.XPATH,r'/html/body/form/div/input').click()
這樣看來(lái),我們iframe也是可以抓取到元素的,不過(guò)要等待頁(yè)面渲染完成
之后再去抓取,否則還是會(huì)報(bào)錯(cuò)的。其余的兩個(gè),因?yàn)樵厥莿?dòng)態(tài)的,如果只是id
的問(wèn)題的話,我們可以使用full xpath
進(jìn)行抓取,但是如果元素嵌套都有可能變化的話,就只能采取截圖比對(duì)的方式了。
此外,我們也再添加一段代碼,比如開(kāi)啟瀏覽器兩個(gè)標(biāo)簽頁(yè)的時(shí)候與標(biāo)簽頁(yè)之間的切換怎么處理:
driver.get("http://www.baidu.com") #打開(kāi)標(biāo)簽1 driver.execute_script("window.open('http://www.baidu.com');") #打開(kāi)標(biāo)簽2 driver.switch_to.window(driver.window_handles[0]) # 手動(dòng)切換到標(biāo)簽1
最后
其實(shí)到這里,python簡(jiǎn)單的控制瀏覽器就已經(jīng)結(jié)束了,其實(shí)圖片的對(duì)比不局限于瀏覽器,它可以截取PC的屏幕上的所有,因此也可以模擬APP的操作,但是我們?cè)谡{(diào)取的過(guò)程中,常常會(huì)報(bào)錯(cuò),但是如果打包成了exe文件,我們并不知道報(bào)的什么錯(cuò),因此我們也可以在最外層加上try-except,如果檢測(cè)到報(bào)錯(cuò),我們可以將報(bào)錯(cuò)內(nèi)容記錄在txt中,模擬日志
的效果,例如:
from datetime import datetime, timedelta try: # some codes except Exception as e: now = datetime.now() date_time = now.strftime("%Y-%m-%d %H:%M:%S") data_to_append = f"{date_time}\n{e}\n\n" with open('C:\\logs\\error.txt', 'a') as file: file.write(data_to_append) print("Error!", e) driver.quit() raise ValueError("Error!")
最后打開(kāi)預(yù)覽報(bào)錯(cuò)信息,例如:
代碼
import pyautogui import pyperclip import time from selenium.webdriver.common.by import By from selenium import webdriver from selenium.webdriver.chrome.service import Service from datetime import datetime, timedelta def cvEnter(msg): pyperclip.copy(msg) time.sleep(1) pyautogui.hotkey('ctrl','v') time.sleep(1) pyautogui.hotkey('enter') try: driver = webdriver.Chrome(service=Service(r"C:\\pyExe\\driver\\chromedriver.exe")) #驅(qū)動(dòng)寫(xiě)在最上面 driver.maximize_window() driver.execute_script("document.charset='utf-8';") driver.get("http://www.baidu.com") driver.implicitly_wait(10) cvEnter("稀土掘金") driver.find_element(By.XPATH,r'//*[@id="su"]').click() #通過(guò)瀏覽器的xpath觸發(fā)點(diǎn)擊事件、 time.sleep(2) driver.find_element(By.XPATH,r'//*[@id="kw"]').clear() driver.find_element(By.XPATH,r'//*[@id="kw"]').send_keys('稀土掘金官網(wǎng)') pyautogui.hotkey('enter') time.sleep(5) except Exception as e: now = datetime.now() date_time = now.strftime("%Y-%m-%d %H:%M:%S") data_to_append = f"{date_time}\n{e}\n\n" with open('C:\\pyExe\\logs\\error.txt', 'a') as file: file.write(data_to_append) print("Error!", e) driver.quit() raise ValueError("Error!")
代碼打包
pyinstaller --onefile --noconsole --icon ./favorite.ico ./index.py # --noconsole 隱藏黑窗口 # --icon ./favorite.ico 打包后的應(yīng)用圖標(biāo)(路徑選對(duì),僅支持ico) # ./index.py 需要打包的py文件路徑
有時(shí)候打包完圖標(biāo)沒(méi)有變化,是默認(rèn)的圖標(biāo),可以查看屬性,屬性中應(yīng)該是已經(jīng)有了的,這是因?yàn)閼?yīng)用緩存的原因,刷新一下應(yīng)用程序即可。
重中之重!?。?/strong>
打包完成之后,將打包后的dist/index.exe文件,移到之前index.py同級(jí)目錄下,因?yàn)槿绻婕暗綀D片的比對(duì),會(huì)導(dǎo)致圖片的位置訪問(wèn)失敗。
以上就是使用python實(shí)現(xiàn)一個(gè)瀏覽器自動(dòng)化的腳本的詳細(xì)內(nèi)容,更多關(guān)于python瀏覽器自動(dòng)化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
一篇文章帶你搞定Ubuntu中打開(kāi)Pycharm總是卡頓崩潰
這篇文章主要介紹了一篇文章帶你搞定Ubuntu中打開(kāi)Pycharm總是卡頓崩潰,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11Python緩存方案優(yōu)化程序性能提高數(shù)據(jù)訪問(wèn)速度
Python緩存方案是一種優(yōu)化程序性能,提高數(shù)據(jù)訪問(wèn)速度的方案。通過(guò)緩存數(shù)據(jù),可以減少重復(fù)的計(jì)算和IO操作,從而提高程序的運(yùn)行效率。Python中常用的緩存方案包括內(nèi)存緩存、磁盤(pán)緩存和分布式緩存等,根據(jù)實(shí)際需求選擇不同的方案可以幫助我們更好地優(yōu)化程序性能2023-05-05總結(jié)Python圖形用戶界面和游戲開(kāi)發(fā)知識(shí)點(diǎn)
在本篇文章里小編給大家整理了關(guān)于Python圖形用戶界面和游戲開(kāi)發(fā)知識(shí)點(diǎn)以及實(shí)例代碼,需要的朋友們學(xué)習(xí)下。2019-05-05Python自動(dòng)檢測(cè)requests所獲得html文檔的編碼
這篇文章主要為大家詳細(xì)介紹了如何通過(guò)Python自動(dòng)檢測(cè)requests實(shí)現(xiàn)獲得html文檔的編碼,文中的示例代碼講解詳細(xì),感興趣的可以了解下2024-11-1110個(gè)頂級(jí)Python實(shí)用庫(kù)推薦
這篇文章主要推薦了10個(gè)頂級(jí)Python實(shí)用庫(kù),幫助大家更好的理解和學(xué)習(xí)實(shí)用python,感興趣的朋友可以了解下2021-03-03