Pyppeteer库之四:Pyppeteer的页面操作(下)
執行自定義的JS腳本
Pyppeteer Page對象提供了一系列evaluate方法,你可以通過他們來執行一些自定義JS代碼,主要提供了下面三個API:
(1) page.evaluate(pageFunction [,...args]), 返回pageFunction執行的結果,pageFunction表示要在頁面執行的函數或表達式,args表示傳入給pageFunction的參數
示例:
await page.goto('https://www.baidu.com') # 輸出字符串 await page.evaluate('alert("在瀏覽器執行js腳本!")') # 將元素作為參數傳入 page.evaluate element = await page.J('#ul>a[name="tj_trtieba"]') print(await page.evaluate('el => el.innerHTML', element)) print(await page.evaluate('el => el.href', element) # 執行函數 el = await page.evaluate('() => document.querySelector("#su").value') print(el)(2) page.evaluateHandle(pageFunction[,...args]), 此方法和page.evaluate的唯一區別是此方法返回的是頁內類型(JSHandle)
示例:
await page.goto('https://www.baidu.com') el = await page.evaluateHandle('() => document.querySelector("#su").value') print(type(el)) print(el.toString())(3) page.evaluateOnNewDocument(pageFunction[, ...args]), 指定的函數在所屬的頁面被創建并且所屬頁面的任意script執行之前被調用。常用于修改頁面JS環境。
以下為插入中間JS,將淘寶會為了檢測瀏覽器而調用的JS修改其結果:
import asyncio from pyppeteer import launchasync def main():browser = await launch({'headless': False,'args': ['--no-sandbox', '--window-size=1366,768']})page = await browser.newPage()await page.setViewPort({'width': 1366, 'height': 768})await page.evaluateOnNewDocument('''() => {Object.defineProperty(navigator, 'webdriver', {get: () => false });}''')await page.goto('https://login.taobao.com')await page.evaluate('alert(navigator.webdriver)')await browser.close()asyncio.get_event_loop().run_until_complete(main())元素操作
ElementHandle表示頁內的DOM元素,你可以通過page.querySelector()方法創建。DOM元素具有和page相同的某些方法: J()、JJ()、Jeval()、JJeval()、screenshot()、type()、click()、tap()。此外,還有一些好用的方法:
(1) 獲取元素邊界框坐標: boundingBox(),返回元素的邊界框(相對于主框架) => x坐標、y坐標、width、height
(2) 元素是否可見: isIntersectingViewport()
(3) 上傳文件: uploadFile(*filepaths)
(4) ElementHandle類轉Frame類:contentFrame(),如果句柄未引用iframe,則返回None。
(5) 聚焦該元素: focus()
(6) 與鼠標相關: hover(),將鼠標懸停到元素上面
(7) 與鍵盤相關: press(key[, options]),按鍵,key表示按鍵的名稱,options可配置:
- text(string) - 如果指定,則使用此文本生成輸入事件
- delay(number) - keydown和keyup之間等待的時間,默認是0
?
鼠標事件
Mouse 類在相對于視口左上角的主框架CSS像素中運行。
(1) page.mouse.down([options]) 按下鼠標,options 可配置:
- button(str) 按下了哪個鍵,可選值為[left, right, middle],默認是left,表示鼠標左鍵
- clickCount(int) 按下的次數,單擊,雙擊或者其他次數
(2) page.mouse.up([options])松開鼠標,options同上
(3) page.mouse.move(x, y, [options])移動鼠標到指定位置,options.steps表示移動的步長
(4) page.mouse.click(x, y, [options])鼠標點擊指定的位置,其實是mouse.move和mouse.down或mouse.up的快捷操作
?
模擬登錄的驗證碼處理
可能用到的方法:
- ElementHandle.boundingBox()、ElementHandle.hover()
- mouse.down()、mouse.move()、mouse.up()、mouse.click()
實例一:淘寶驗證碼 拖動滑塊
(1) 淘寶的驗證碼驗證模塊會檢測瀏覽器環境,要注入JS;
(2) 盡可能模擬用戶操作,隨機數減慢Pyppeteer的執行速度;
示例:
import asyncio import random from pyppeteer import launchasync def main():browser = await launch({'headless': False,'args': ['--no-sandbox', '--window-size=1366,768']})page = await browser.newPage()await page.setViewport({'width': 1366, 'height': 768})await page.evaluateOnNewDocument('''() => {Object.defineProperties(navigator, { webdriver:{ get: () => false}}}''')await page.evaluateOnNewDocument('''() => {window.navigator.chrome = { runtime: {}, };}''')await page.evaluateOnNewDocument('''() => {Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });}''')await page.evaluateOnNewDocument('''() => {Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')await page.goto('https://login.taobao.com')await asyncio.sleep(2)try:await page.click('div.login-links > a.forget-pwd.J_Quick2Static')except:passawait asyncio.sleep(2)await page.type('#TPL_username_1', '123123123', {'delay': random.randint(60, 121)})await page.type('#TPL_password_1', '1234567890', {'delay': random.randint(100, 151)})await asyncio.sleep(1.5)try:el = await page.querySelector('#nc_1_n1z')box = await el.boundingBox()await page.hover('#nc_1_n1z')await page.mouse.down()await page.mouse.move(box['x'] + random.randint(333, 999), box['y'], {'steps': 5})await page.mouse.up()except:passawait asyncio.sleep(1.8)await page.click('#J_SubmitStatic')await asyncio.sleep(5)await browser.close()asyncio.get_event_loop().run_until_complete(main())實例二:鐵路12306點觸驗證碼
(1) 分析12306的驗證碼;這個東西是長這樣的:
鼠標點擊的位置,可以取值各個圖片的中心點:
這個值可以計算:
- width: 37, 37 * 3, 37 * 5, 37 * 7; 即37, 111, 185, 259
- height(0): 70
- height(1): 70 + (190-30)/2, 即150
當驗證碼圖片的坐標為x,y時;鼠標點擊第2,7張圖片的位置可以表達為(x+111, y+70),(x+185, y+150)
示例:
import asyncio import random from pyppeteer import launchasync def main():browser = await launch({'headless': False,'args': [f'--window-size=1366,768', '--no-sandbox']})page = await browser.newPage()await page.goto('https://kyfw.12306.cn/otn/login/init',{'waitUntil': 'networkidle0'})await page.setViewport({'width': 1366, 'height': 768})# 等待驗證碼加載code = await page.waitForFunction('''() => document.querySelector("img.touclick-image")''')# 驗證碼截圖await code.screenshot({'path': 'code.png'})# 獲取驗證碼坐標box = await code.boundingBox()await page.waitFor(2 * 1000)# 點擊第2張圖片await page.mouse.click(box['x']+111, box['y']+70)await page.waitFor(random.randint(567, 3456))# 點擊第7張圖片await page.mouse.click(box['x']+185, box['y'] + 150)await page.waitFor(3 * 1000)await browser.close()asyncio.get_event_loop().run_until_complete(main())(2) 打碼平臺:12306的
驗證碼識別有點反人類;對接打碼平臺是比較不錯的選擇;原理就是把驗證碼圖片以字節的方式發給他們,返回一個字符串,例如:183,68|193,161;
超級鷹打碼平臺API:
chaojiying.py
#!/usr/bin/env python # coding:utf-8import requests from hashlib import md5class CodeInfo(object):def __init__(self):self.username = '用戶名'self.password = md5('密碼'.encode('utf8')).hexdigest()self.soft_id = '96001' # 用戶中心 >> 軟件ID,生成一個替換96001self.base_params = {'user': self.username,'pass2': self.password,'softid': self.soft_id,}self.headers = {'Connection': 'Keep-Alive','User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',}def process(self, im, codetype):url = 'http://upload.chaojiying.net/Upload/Processing.php'params = {'codetype': codetype}params.update(self.base_params)files = {'userfile': ('ccc.jpg', im)}r = requests.post(url, data=params, files=files, headers=self.headers)return r.json()def report(self, im_id):"""im_id:報錯題目的圖片ID"""url = 'http://upload.chaojiying.net/Upload/ReportError.php'params = {'id': im_id}params.update(self.base_params)r = requests.post(url, data=params, headers=self.headers)return r.json()if __name__ == '__main__':im = open('code.png', 'rb').read()"""9004 驗證碼類型參考 http://www.chaojiying.com/price.html"""answer = CodeInfo().process(im, 9004)print(answer)登錄12306的例子
import asyncio import random from pyppeteer import launch from chaojiying import CodeInfodef pic_info():im = open('code.png', 'rb').read()answer = CodeInfo().process(im, 9004)print(answer)return answer['pic_str']async def main():browser = await launch({'headless': False,'args': ['--window-size=1366,768', '--no-sandbox']})page = await browser.newPage()await page.goto('https://kyfw.12306.cn/otn/login/init',{'waitUntil': 'networkidle0'})await page.setViewport({'width': 1366, 'height': 768})code =await page.waitForFunction('''() => document.querySelector("img.touclick-image")''')await code.screenshot({'path': 'code.png'})await page.waitFor(2 * 1000)await page.type('#username', '123456789@qq.com',{'delay': random.randint(60, 121)})await page.waitFor(random.randint(345, 1234))await page.type('#password', '1234567890',{'delay': random.randint(100, 151)})pic_str = pic_info()points = list(set(pic_str.split('|')))box = await code.boundingBox()for point in points:p = point.split(',')await page.mouse.click(box['x']+int(p[0]), box['y']+int(p[1]))await page.waitFor(random.randint(567, 3456))await page.click('#loginSub')await page.waitFor(5 * 1000)await browser.close()asyncio.get_event_loop().run_until_complete(main())鍵盤事件
Keyboard 提供一個接口來管理虛擬鍵盤. 高級接口為keyboard.type,其接收原始字符,然后在你的頁面上生成對應的keydown,keypress/input,和keyup事件。
為了更精細的控制(虛擬鍵盤),你可以使用keyboard.down, keyboard.up和keyboard.sendCharacter來手動觸發事件,就好像這些事件是由真實的鍵盤生成的。
鍵盤的幾個API如下:
- keyboard.down(key[, options])觸發keydown事件
- keyboard.press(key[, options])按下某個鍵,key表示鍵的名稱,比如'ArrowLeft'向左鍵;
- keyboard.sendCharacter(char)輸入一個字符
- keyboard.type(text, options)輸入一個字符串
- keyboard.up(key)觸發keyup事件
?
持續按下shift來選擇一些字符串并且刪除的例子:
import asyncio from pyppeteer import launchasync def main():browser = await launch({'headless': False})page = await browser.newPage()await page.goto('https://www.baidu.com', {'waitUntil': 'networkidle0'})el = await page.J('#kw')await el.focus()await page.keyboard.type('Hello, World!')await page.keyboard.press('ArrowLeft')await page.keyboard.down('Shift')for _ in ' World':await page.keyboard.press('ArrowLeft')await page.keyboard.press('ArrowLeft')await page.keyboard.up('Shift')await page.keyboard.press('Backspcae')# 結果字符串最終為'Hello!'await asyncio.sleep(5)await browser.close()asyncio.get_event_loop().run_until_complete(main())按下?A的例子:
await page.keyboard.down('Shift') await page.keyboard.press('KeyA') await page.keyboard.up('Shift')詳細的健名映射可以看源碼:
Lib\site-packages\pyppeteer\us_keyboard_layout.py
內嵌框架
可以通過 Page.frames、ElementHandle.contentFrame方法獲取,同時具有和page多個方法;
**其它:
- childFrames 獲取子框架,返回列表
- parentFrame 返回父框架
- content() 返回框架的 html 內容
- url 獲取 url
- name 獲取 name
- title() 獲取 title
?
例子:
import asyncio from pyppeteer import launchasync def main():browser = await launch({'headless': False})page = await browser.newPage()await page.goto('http://www.4399.com', {'waitUntil': 'networkidle0'})await page.click('#login_tologin')await asyncio.sleep(1)frame = page.frames[1]await frame.type('#username', '123456789')await frame.type('#j-password', '998765433')await asyncio.sleep(5)await browser.close()asyncio.get_event_loop().run_until_complete(main())或者:
await page.click('#login_tologin') await asyncio.sleep(1) element = await page.J('iframe') frame = await element.contentFrame()?
總結
以上是生活随笔為你收集整理的Pyppeteer库之四:Pyppeteer的页面操作(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自动化系列-pyppeteer键盘输入点
- 下一篇: 瑞数(裁判文书)js的加密分析