背景
之前我在工作中涉及到了很多地方都是重復(fù)性的頁(yè)面點(diǎn)點(diǎn)點(diǎn)工作,又因?yàn)榘踩C茉瓌t不開(kāi)放接口和數(shù)據(jù)庫(kù),只有一個(gè)頁(yè)面來(lái)提供點(diǎn)擊進(jìn)行操作,就想著用前面學(xué)的自動(dòng)化來(lái)實(shí)現(xiàn),但發(fā)現(xiàn)前面學(xué)的模擬操作對(duì)瀏覽器來(lái)說(shuō)并沒(méi)有那么友好,而后改用“selenium”,但是存在一個(gè)問(wèn)題,我這里并不能直接訪問(wèn)外網(wǎng),好不容易找到selenium的庫(kù)文件,發(fā)現(xiàn)又需要相對(duì)應(yīng)版本的瀏覽器引擎,導(dǎo)致我無(wú)法使用,在此期間我發(fā)現(xiàn)了另一個(gè)不需要瀏覽器引擎的庫(kù)pyppeteer 成功實(shí)現(xiàn)了一部分功能,這里做一下筆記
?介紹
1、selenium //跨瀏覽器,官方維護(hù)的比較好,資料也多,各個(gè)版本比較穩(wěn)定,源碼讀起來(lái)舒服
//缺點(diǎn)是配置時(shí)需要留心程序語(yǔ)言的版本和驅(qū)動(dòng)版本以及瀏覽器版本,還有就是本身不支持步
//需要重寫(xiě)源碼或者利用grid分布式來(lái)實(shí)現(xiàn)異步
2、pyppeteer //是基于chrome官方為chromium定制的自動(dòng)化測(cè)試框架puppeteer而
//實(shí)現(xiàn)的一個(gè)python包裝的非官方版本框架,最后一次更新是在2018年
//優(yōu)點(diǎn)就是速度比selenium快,支持異步,常被拿來(lái)做爬蟲(chóng),
//缺點(diǎn)就是兼容性很差,而且它沒(méi)有跟隨chromium以及puppeteer的迭代而更新
//使用時(shí)會(huì)有很多問(wèn)題。
安裝
pip install pyppeteer==1.0.2
入門(mén)案例
我們打開(kāi)瀏覽器、輸入、點(diǎn)擊按鈕什么的都是是耗時(shí)的操作,我們下面通過(guò)使用異步關(guān)鍵字?
async
?和?await
,定義了一個(gè)異步函數(shù)?main
。通過(guò)在異步函數(shù)中使用?await
?關(guān)鍵字,可以將耗時(shí)的操作轉(zhuǎn)化為非阻塞的異步調(diào)用
import asyncio # 導(dǎo)入 asyncio 模塊,用于編寫(xiě)異步代碼
from pyppeteer import launch # 導(dǎo)入 pyppeteer 的 launch 函數(shù),用于啟動(dòng)瀏覽器
async def main(): # 定義一個(gè)異步函數(shù) main
asyncio.get_event_loop().run_until_complete(main()) # 運(yùn)行 main 函數(shù)
一、定義瀏覽器并打開(kāi)頁(yè)面
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(executablePath="C:\Program Files\Google\Chrome\Application\chrome.exe",headless=False,args=['--start-maximized'])
page = await browser.newPage()
await page.setViewport({'width':0,'height':0,'deviiceScaleFactor':1})
await page.goto('https://www.baidu.com')
await page.waitFor(10000)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
參數(shù)說(shuō)明
async def main():
browser = await launch(executablePath="C:\Program Files\Google\Chrome\Application\chrome.exe",headless=False,args=['--start-maximized'])
#launch 定義一個(gè)瀏覽器實(shí)例
#executablePath 本地谷歌瀏覽器路徑
#headless=False 有界面的瀏覽器
#args=['--start-maximized']瀏覽器窗口最大化
//在瀏覽器上創(chuàng)建一個(gè)新頁(yè)面
page = await browser.newPage()
//width 和height 自動(dòng)匹配瀏覽器大小
//deviiceScaleFactor 將頁(yè)面的視口設(shè)置為瀏覽器的默認(rèn)大小,并將設(shè)備像素比設(shè)置為 1
await page.setViewport({'width':0,'height':0,'deviiceScaleFactor':1})
//打開(kāi)瀏覽器并跳轉(zhuǎn)到指定地址
await page.goto('https://www.google.com')
//上面任務(wù)結(jié)束后等待10s
await page.waitFor(10000)
//關(guān)閉瀏覽器
await browser.close()
我們這里能打開(kāi)瀏覽器并且跳轉(zhuǎn)了,下面我們正常情況下需要做的就是模擬鼠標(biāo)鍵盤(pán)的一些操作,我們這里以碼云的登錄注冊(cè)平臺(tái)來(lái)做測(cè)試
#注冊(cè)地址,可能有變動(dòng)直接百度搜索
https://gitee.com/signup?redirect_to_url=%2F%3Fchannel_utm_content%3D%25E5%25B9%25BF%25E5%2591%258A%25E8%2583%258C%25E6%2599%25AF%25E5%259B%25BE%26channel_utm_medium%3Dsem%26channel_link_type%3Dweb%26channel_utm_source%3D%25E7%2599%25BE%25E5%25BA%25A6%26sat_cf%3D2%26channel_utm_campaign%3D%25E5%2593%2581%25E4%25B8%2593%26channel_utm_term%3D%25E5%25B9%25BF%25E5%2591%258A%25E8%2583%258C%25E6%2599%25AF%25E5%259B%25BE%26_channel_track_key%3Du1BDg7fB%26link_version%3D1%26wl_src%3Dbaidu
二、尋找頁(yè)面元素信息
瀏覽器頁(yè)面根據(jù)我們分辨率大小和窗口大小,跳轉(zhuǎn)瀏覽器的位置都會(huì)導(dǎo)致我們無(wú)法直接通過(guò)之前的方法獲取坐標(biāo),這里我們依賴(lài)的是直接獲取web頁(yè)面的元素信息(span a dir id class等等)通過(guò)他們定位具體的元素坐標(biāo)
登錄頁(yè)面查看元素
我們登錄到注冊(cè)頁(yè)面按F12 進(jìn)入開(kāi)發(fā)者模式,點(diǎn)擊查看欄能看到html的信息,點(diǎn)擊左邊的箭頭,選擇我們要查看的頁(yè)面元素,下面圖中是選擇了第一個(gè)輸入框的位置
?
?得到html信息
<input class="session-register__name" required="required" placeholder="姓名" maxlength="60" size="60" type="text" name="user[name]" id="user_name">
三、常見(jiàn)的幾種獲取元素坐標(biāo)方法
各個(gè)場(chǎng)景的html編寫(xiě)的不相同,同一種方法切換場(chǎng)景后很可能就不好使了,這里放幾種我常用的方法
1、通過(guò)元素class或id獲取坐標(biāo)
#基于class名稱(chēng)獲取坐標(biāo)
async def click_radio(page,selector):
await page.waitForSelector(selector) #等待元素出現(xiàn)
element = await page.querySelector(selector) #查找指定的元素信息
if element:
box = await element.boundingBox() #獲取元素坐標(biāo)和尺寸
x = box['x']
y = box['y']
widht = box['width']
height = box['height']
await page.mouse.move(x + widht / 2, y + height / 2)
await page.mouse.down() #模擬鼠標(biāo)點(diǎn)擊一次
await page.mouse.up()
else:
print("element not found")
調(diào)用函數(shù)
async def main():
...
#修改地址為碼云
await page.goto('https://gitee.com/signup?redirect_to_url=%2F%3Fchannel_utm_content%3D%25E8%25BF%259B%25E5%2585%25A5%25E5%25AE%2598%25E7%25BD%2591%26channel_utm_medium%3Dsem%26channel_link_type%3Dweb%26channel_utm_source%3D%25E7%2599%25BE%25E5%25BA%25A6%26sat_cf%3D2%26channel_utm_campaign%3D%25E5%2593%2581%25E4%25B8%2593%26channel_utm_term%3D%25E4%25B8%25BB%25E6%258C%2589%25E9%2592%25AE1%26_channel_track_key%3Dsee7zmAJ%26link_version%3D1%26wl_src%3Dbaidu')
#調(diào)用自定義函數(shù),傳參page class名稱(chēng)前面要加點(diǎn)"."
await click_radio(page,".session-register__name")
#模擬輸入文本信息
await page.keyboard.type("12345678")
...
asyncio.get_event_loop().run_until_complete(main())
注意
上面案例中使用的是class獲取的坐標(biāo),如果沒(méi)有定義class或者有多個(gè)相同的class時(shí)可以通過(guò)id獲取,區(qū)別在于class傳參是 "." 加class名稱(chēng)?? 而id傳參是? "#" 加id名稱(chēng)
小知識(shí)
//其實(shí)在遇到不是特別復(fù)雜的情況下,可以不用上面的方法,比如輸入賬戶密碼之類(lèi)的
await page.waitForSelector(#id名稱(chēng)/.類(lèi)名)
2、通過(guò)文本獲取坐標(biāo)
這個(gè)在申請(qǐng)某些東西的時(shí)候可能會(huì)經(jīng)常用到,比如申請(qǐng)?jiān)品?wù),某某產(chǎn)品,自研平臺(tái)等等,會(huì)有大量需要挨個(gè)點(diǎn)擊的圖標(biāo),用上面第一個(gè)的時(shí)候不好使了就用這個(gè)方法
獲取按鈕html
<button name="button" type="submit" id="btn-submit" class="ui orange fluid submit button register-btn-submit large" sa_evt="click_GiteeCommunity_signup_signup">立即注冊(cè)</button>
?向上面有文本內(nèi)容顯示的就可以用,我們獲取到他的文本內(nèi)容"立即注冊(cè)" 和元素名稱(chēng)button
async def click_center(page,selector,type):
#定義檢索元素格式
test = "http://" + type +"[text()=" + "\'" + selector + "\'" + "]"
element = await page.waitForXPath(test) #獲取對(duì)應(yīng)元素
box = await element.boundingBox() #獲取坐標(biāo)
target_x = box['x'] + box['width'] // 2
target_y = box['y'] + box['height'] // 2
await page.mouse.click(target_x,target_y)
調(diào)用
async def main():
browser = await launch(executablePath="C:\Program Files\Google\Chrome\Application\chrome.exe",headless=False,args=['--start-maximized'])
page = await browser.newPage()
await page.setViewport({'width':0,'height':0,'deviiceScaleFactor':1})
await page.goto('https://gitee.com/signup?redirect_to_url=%2F%3Fchannel_utm_content%3D%25E8%25BF%259B%25E5%2585%25A5%25E5%25AE%2598%25E7%25BD%2591%26channel_utm_medium%3Dsem%26channel_link_type%3Dweb%26channel_utm_source%3D%25E7%2599%25BE%25E5%25BA%25A6%26sat_cf%3D2%26channel_utm_campaign%3D%25E5%2593%2581%25E4%25B8%2593%26channel_utm_term%3D%25E4%25B8%25BB%25E6%258C%2589%25E9%2592%25AE1%26_channel_track_key%3Dsee7zmAJ%26link_version%3D1%26wl_src%3Dbaidu')
#調(diào)用函數(shù),定義文本+元素并點(diǎn)擊
await click_center(page,"立即注冊(cè)","button")
await page.waitFor(10000)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
?測(cè)試通過(guò)上面?zhèn)z方法能解決絕大部分問(wèn)題,如果遇到的坐標(biāo)有偏移,比如說(shuō)我文本輸入框在文本的右側(cè),我通常會(huì)用下面的方法
target_x = box['x'] + box['width'] + 200
target_y = box['y'] + box['height'] // 2
小知識(shí)
當(dāng)你遇到點(diǎn)擊某個(gè)按鈕后會(huì)跳轉(zhuǎn)到下一步的時(shí)候,最好按鈕按下后面的第一個(gè)步驟中添加 await page.waitForNavigation()? 這個(gè)是等等頁(yè)面加載完成
3、修改頁(yè)面元素,然后基于頁(yè)面元素輸入
特殊情況下,上面的方法都不適用,比如說(shuō)我遇到過(guò)一個(gè)常見(jiàn),需要申請(qǐng)兩個(gè)文件系統(tǒng)/app1 和/app2? 一個(gè)50G? 一個(gè)100G ,但他的輸入框上只有一個(gè)class,并且每個(gè)class都是完全已有的,沒(méi)有id什么其他的元素,特征就是初始值為一個(gè)輸入框,可以新增一個(gè)輸入框,我的想法我把第一個(gè)輸入框的元素class進(jìn)行修改,第二個(gè)元素出現(xiàn)時(shí)class類(lèi)就和第一個(gè)元素不同了,然后基于這個(gè)修改后的class名稱(chēng)在做具體的操作
我這里通過(guò)密碼那一欄獲取到下面的代碼
<input required="required" autocomplete="new-password" placeholder="密碼不少于6位" data-password-regx="^(?=.*[0-9])(?=.*[a-zA-Z!@_#$%^&*()\-+=,.?]).{6,32}$" type="password" name="user[password]" id="user_password">
可以看到,他有一個(gè)id,但是沒(méi)有做class我們用這個(gè)做實(shí)驗(yàn)
?案例
await page.evaluate('''() => {
const elements = document.querySelectorAll('#user_password');
elements.forEach(element => {
element.classList.add("ddd");
});
}''')
#我們需要等等元素出現(xiàn)后在進(jìn)行下面的操作
await page.waitForSelector(".ddd")
...
#另外,單獨(dú)說(shuō)個(gè)事,如果切換頁(yè)面后邏輯中存在多個(gè)等待頁(yè)面加載完成,那么頁(yè)面就不動(dòng)了
可以看到上面圖里他幫忙添加了一個(gè)class的名稱(chēng),我們可以在后面去調(diào)用他,需要注意的是,如果你要添加的元素的id不唯一那么所有的元素都會(huì)去添加相同的class,如果碰到了多個(gè)class名稱(chēng),如? app1 app2 app3??? class的名稱(chēng)則需要設(shè)置為 .app1 .app2 .app3? 如果是id則是 #app1? #app2 #app3
4、查找特定文本元素并點(diǎn)擊
我又碰到一個(gè)特殊的案例,我沒(méi)找到演示用的頁(yè)面,當(dāng)記錄下筆記了,這個(gè)是應(yīng)用于li的一個(gè)下拉選項(xiàng)的場(chǎng)景,和第二步類(lèi)似
async def click_multiple(page,selector):
elements = await page.JJ('li')
for element in elements:
text_content = await element.getProperty("textContent")
text_content = await text_content.jsonValue()
if selector in text_content:
box = await element.boundingBox()
coordinates = {
'x': box['x'],
'y': box['y'],
'width': box['width'],
'height': box['height']
}
await element.click()
上面的函數(shù)是查找頁(yè)面所有的?
li
?元素,并檢查每個(gè)元素的文本內(nèi)容中是否包含給定的選擇器。如果找到了匹配的元素,則對(duì)該元素執(zhí)行點(diǎn)擊操作。
參數(shù)說(shuō)明
async def click_multiple(page, selector):
elements = await page.JJ('li') # 查找頁(yè)面中所有的 <li> 元素并保存在 elements 變量中
for element in elements: # 遍歷 elements 中的每個(gè)元素
text_content = await element.getProperty("textContent") # 獲取元素的文本內(nèi)容
text_content = await text_content.jsonValue() # 將文本內(nèi)容轉(zhuǎn)換為 JSON 格式
if selector in text_content: # 檢查文本內(nèi)容是否包含給定的選擇器
box = await element.boundingBox() # 獲取元素的位置和大小
coordinates = {
'x': box['x'], # 元素的 x 坐標(biāo)
'y': box['y'], # 元素的 y 坐標(biāo)
'width': box['width'], # 元素的寬度
'height': box['height'] # 元素的高度
}
await element.click() # 點(diǎn)擊元素
四、讀取excel表數(shù)據(jù)并使用
def open_xlsx():
from openpyxl import load_workbook
wb = load_workbook("111.xlsx")
ws = wb["Sheet1"]
data = []
for row in ws.iter_rows(min_row=2): #從第二行開(kāi)始算
row_values = []
for cell in row:
row_values.append(cell.value)
data.append(row_values)
wb.close()
return data
data = open_xlsx()
#這里的user_list取出來(lái)的是表中每一行的數(shù)據(jù),下面的0-1-2-3是每一列的數(shù)據(jù)
for user_list in data:
print(user_list[0])
說(shuō)明文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-661072.html
def open_xlsx():
from openpyxl import load_workbook
# 打開(kāi) Excel 文件
wb = load_workbook("111.xlsx")
# 選擇要讀取的工作表
ws = wb["Sheet1"]
# 創(chuàng)建一個(gè)空的列表用于存儲(chǔ)讀取的數(shù)據(jù)
data = []
# 從第二行開(kāi)始遍歷每一行
for row in ws.iter_rows(min_row=2):
row_values = []
# 遍歷當(dāng)前行的每一個(gè)單元格
for cell in row:
# 將單元格的值添加到行值列表中
row_values.append(cell.value)
# 將該行的值列表添加到數(shù)據(jù)列表中
data.append(row_values)
# 關(guān)閉 Excel 文件
wb.close()
# 返回讀取的數(shù)據(jù)
return data
# 調(diào)用 open_xlsx 函數(shù)并獲取數(shù)據(jù)
data = open_xlsx()
# 遍歷數(shù)據(jù)列表的每一行,并打印出每行的第一列數(shù)據(jù)
for user_list in data:
print(user_list[0])
隔壁有個(gè)寫(xiě)的不錯(cuò)的收藏下文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-661072.html
https://blog.csdn.net/qq_27648991/article/details/105329455?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EOPENSEARCH%7ERate-1-105329455-blog-132301111.235%5Ev38%5Epc_relevant_anti_t3&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EOPENSEARCH%7ERate-1-105329455-blog-132301111.235%5Ev38%5Epc_relevant_anti_t3&utm_relevant_index=1
到了這里,關(guān)于python 自動(dòng)化學(xué)習(xí)(四) pyppeteer 瀏覽器操作自動(dòng)化的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!