使用python實(shí)現(xiàn)一個(gè)瀏覽器自動(dòng)化的腳本
背景
最近工作中有這樣一個(gè)需求:客戶反饋在瀏覽器操作過程中,重復(fù)流程操作太頻繁,能不能讓瀏覽器自動(dòng)操作完成?
在我的認(rèn)知中,瀏覽器就是一個(gè)用來瀏覽頁面的工具,因此第一反應(yīng)就是“不可能!絕對(duì)不可能!”。領(lǐng)導(dǎo)說,你了解過RPA嗎?會(huì)后專門查閱了相關(guān)資料,在我的理解中,RPA就是一個(gè)定制化機(jī)器人(腳本流程),通過控制腳本操作顯示器,來模擬用戶操作的過程。
開始
一開始的時(shí)候,我在github上面找了一些開源的項(xiàng)目,例如:PyRPA、RPA-Python、TagUI、openrpa等,但是因?yàn)槎ㄖ苹容^強(qiáng),后期不知道會(huì)變成怎么樣,因此做的要靈活一些。但是在這些開源項(xiàng)目中,提供了一部分的api,但是在有些地方并不是很容易操作,感覺后面可能會(huì)不可控。
但是在我觀察中發(fā)現(xiàn),大多數(shù)的實(shí)現(xiàn)都是通過python實(shí)現(xiàn)的,作為一個(gè)前端,第一反應(yīng)就是,為什么不用node和js結(jié)合呢,最后反應(yīng)過來,使用python可以操作整個(gè)PC,但是如果你使用node或js,只能說操作瀏覽器更方便一些了(其實(shí)和python差不多),只是python在使用第三方庫時(shí)候,會(huì)存在一定程度的延時(shí)。
因此,我決定放棄現(xiàn)有的RPA的項(xiàng)目,自己實(shí)現(xiàn)一個(gè)自動(dòng)化的python腳本。
語言選擇
關(guān)于我為什么選擇的python作為語言,其實(shí)在前面已經(jīng)說過了,作為一個(gè)前端開發(fā),其實(shí)最熟悉的莫過于javascript,另加一些nodejs,那么為什么我沒有用nodejs呢?node在前端開發(fā)過程中,常常扮演著一個(gè)環(huán)境的角色,雖然也可以作為一個(gè)后端語言來寫一些接口或服務(wù)(依賴于第三方庫),但是很少用到DOM的操作,操作瀏覽器的DOM,我們通常來說,都是使用js直接操作,或使用jquery等第三方工具。如果要再直接去攔截系統(tǒng)的一些操作,js就不太行了。
因此,采取了python,這一門熱度一直很高的語言。主要思路就是,圖片的對(duì)比,獲取點(diǎn)位,模擬用戶鼠標(biāo),鍵盤等操作。
瀏覽器的啟動(dòng)
其實(shí)這個(gè)啟動(dòng)瀏覽器,一開始我只是單純的打開瀏覽器,但是后面發(fā)現(xiàn),每天第一次啟動(dòng)的時(shí)候,都會(huì)特別慢,查閱資料也沒有找到原因,好像是緩存被清空了吧,網(wǎng)上找了大量的相關(guān)資料,最終找到了一個(gè)方案:加載時(shí)候啟動(dòng)指定驅(qū)動(dòng)程序(瀏覽器版本與驅(qū)動(dòng)程序需要對(duì)應(yīng)),指定版本瀏覽器下載,新版驅(qū)動(dòng)下載,114版本前驅(qū)動(dòng)下載,有一部分頁面需要科學(xué)上網(wǎng)才能訪問與下載,建議大家科學(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)可以打開瀏覽器,并操作瀏覽器調(diào)取到百度的頁面了,我們想在百度的頁面輸入并搜索指定內(nèi)容怎么辦呢?這時(shí)候我們就需要獲取頁面的元素節(jié)點(diǎn),然后進(jìn)行輸入了,下面我們來看一下如何在瀏覽器搜索吧!
在百度進(jìn)行搜索的方案
首先我們先列舉一下目前能夠想到的兩個(gè)方案:
- 從頁面抓取頁面
元素路徑,然后直接點(diǎn)擊元素; - 整個(gè)頁面截圖,再截取需要點(diǎn)擊的小圖片,獲取需要點(diǎn)擊的
圖片的坐標(biāo),點(diǎn)擊坐標(biāo);
這兩種方案對(duì)于簡單的操作都是可行的,復(fù)雜一些的,會(huì)出現(xiàn)一些問題,我們先看一下兩種方案分別怎么進(jìn)行操作,然后再來分析一下會(huì)遇見怎樣的問題吧!
頁面抓取元素,直接點(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的定義在本文最上面,這里沒有重復(fù)寫
#...
cvEnter("稀土掘金")
driver.find_element(By.XPATH,r'//*[@id="su"]').click() #通過瀏覽器的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)使用了兩種輸入與搜索的方式:
輸入
- 方式一:通過
粘貼板,直接進(jìn)行“c+v”; - 方式二:借助python瀏覽器的driver的
send_keys方法,直接鍵入;
搜索(這里是回車和點(diǎn)擊實(shí)現(xiàn)的都是搜索)
- 方式一:使用pyautogui的
鍵盤熱鍵觸發(fā); - 方式二:借助python瀏覽器的driver獲取瀏覽器
元素,觸發(fā)該元素的click事件;
通過圖片的對(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庫,實(shí)現(xiàn)了一個(gè)簡單的大圖中尋找小圖的功能,這里在獲取之前自動(dòng)截圖并保存到指定目錄,小圖的名稱作為參數(shù),傳遞到封裝的方法中,抓取點(diǎn)位信息,再點(diǎn)擊獲取到的坐標(biāo)位置。因?yàn)槲覀冏ト〉淖鴺?biāo)點(diǎn),因此如果點(diǎn)擊的輸入框,輸入框粘貼數(shù)據(jù),就需要直接通過粘貼板進(jìn)行粘貼了。
為什么我們前面說抓取元素會(huì)有問題呢?
首先我們最理想的元素抓取就是,都在一個(gè)html中,沒有動(dòng)態(tài)的元素,沒有切換界面,沒有切換彈窗等,但是在實(shí)際的應(yīng)用過程中,往往都是比較復(fù)雜的,例如:
vue打包的html(元素的id不是固定的,有時(shí)候元素節(jié)點(diǎn)渲染的問題,嵌套可能有點(diǎn)區(qū)別);html嵌套了iframe(直接切換了代碼引用的窗口,后面都統(tǒng)一叫做句柄);選擇框的元素,展開和關(guān)閉不一致;- ...
以上的問題,都會(huì)導(dǎo)致直接抓取元素xpath直接調(diào)取時(shí)候報(bào)錯(cuò),難道我們就真的不能用xpath進(jìn)行操作了嗎?答案當(dāng)然是否定的,但是也不是完全,例如在iframe中,我們可以切換句柄,來達(dá)到代碼引用模塊的更換,例如:
#driver的定義在本文最上面,這里沒有重復(fù)寫 #... 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()
這樣看來,我們iframe也是可以抓取到元素的,不過要等待頁面渲染完成之后再去抓取,否則還是會(huì)報(bào)錯(cuò)的。其余的兩個(gè),因?yàn)樵厥莿?dòng)態(tài)的,如果只是id的問題的話,我們可以使用full xpath進(jìn)行抓取,但是如果元素嵌套都有可能變化的話,就只能采取截圖比對(duì)的方式了。
此外,我們也再添加一段代碼,比如開啟瀏覽器兩個(gè)標(biāo)簽頁的時(shí)候與標(biāo)簽頁之間的切換怎么處理:
driver.get("http://www.baidu.com") #打開標(biāo)簽1
driver.execute_script("window.open('http://www.baidu.com');") #打開標(biāo)簽2
driver.switch_to.window(driver.window_handles[0]) # 手動(dòng)切換到標(biāo)簽1
最后
其實(shí)到這里,python簡單的控制瀏覽器就已經(jīng)結(jié)束了,其實(shí)圖片的對(duì)比不局限于瀏覽器,它可以截取PC的屏幕上的所有,因此也可以模擬APP的操作,但是我們?cè)谡{(diào)取的過程中,常常會(huì)報(bào)錯(cuò),但是如果打包成了exe文件,我們并不知道報(bào)的什么錯(cuò),因此我們也可以在最外層加上try-except,如果檢測到報(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!")
最后打開預(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)寫在最上面
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() #通過瀏覽器的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)沒有變化,是默認(rèn)的圖標(biāo),可以查看屬性,屬性中應(yīng)該是已經(jīng)有了的,這是因?yàn)閼?yīng)用緩存的原因,刷新一下應(yīng)用程序即可。
重中之重!??!
打包完成之后,將打包后的dist/index.exe文件,移到之前index.py同級(jí)目錄下,因?yàn)槿绻婕暗綀D片的比對(duì),會(huì)導(dǎo)致圖片的位置訪問失敗。
以上就是使用python實(shí)現(xiàn)一個(gè)瀏覽器自動(dòng)化的腳本的詳細(xì)內(nèi)容,更多關(guān)于python瀏覽器自動(dòng)化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python?pickle模塊實(shí)現(xiàn)Python對(duì)象持久化存儲(chǔ)
這篇文章主要介紹了Python?pickle模塊實(shí)現(xiàn)Python對(duì)象持久化存儲(chǔ),pickle?是?python?語言的一個(gè)標(biāo)準(zhǔn)模塊,和python安裝時(shí)共同安裝好的一個(gè)模塊。下文基于pickle模塊展開實(shí)現(xiàn)Python對(duì)象持久化存儲(chǔ)的詳細(xì)內(nèi)容,需要的朋友可以參考一下2022-05-05
pymysql 插入數(shù)據(jù) 轉(zhuǎn)義處理方式
今天小編就為大家分享一篇pymysql 插入數(shù)據(jù) 轉(zhuǎn)義處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03
python list中append()與extend()用法分享
列表是以類的形式實(shí)現(xiàn)的?!皠?chuàng)建”列表實(shí)際上是將一個(gè)類實(shí)例化。因此,列表有多種方法可以操作2013-03-03
Python基礎(chǔ)之enumerate()函數(shù)用法詳解
enumerate()函數(shù)用于將一個(gè)可遍歷的數(shù)據(jù)對(duì)象(如列表、元組或字符串)組合為一個(gè)索引序列,同時(shí)列出數(shù)據(jù)和數(shù)據(jù)下標(biāo),一般用在for循環(huán)當(dāng)中,這篇文章主要介紹了Python基礎(chǔ)之enumerate()函數(shù)用法的相關(guān)資料,需要的朋友可以參考下2025-12-12
通過celery異步處理一個(gè)查詢?nèi)蝿?wù)的完整代碼
今天小編就為大家分享一篇通過celery異步處理一個(gè)查詢?nèi)蝿?wù)的完整代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-11-11
Python?numpy下幾種fft函數(shù)的使用方式
numpy中有一個(gè)fft的庫,scipy中也有一個(gè)fftpack的庫,各自都有fft函數(shù),兩者的用法基本是一致的,下面這篇文章主要給大家介紹了關(guān)于Python?numpy下幾種fft函數(shù)的使用方式,需要的朋友可以參考下2022-08-08
python pands實(shí)現(xiàn)execl轉(zhuǎn)csv 并修改csv指定列的方法
今天小編就為大家分享一篇python pands實(shí)現(xiàn)execl轉(zhuǎn)csv 并修改csv指定列的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-12-12

