最近開始上手一個自動化測試工具Puppeteer,來談一談關於它的一些事兒。html
Puppeteer中文文檔:https://zhaoqize.github.io/puppeteer-api-zh_CN/#?product=Puppeteer&version=v1.20.0&show=api-class-puppeteer(推薦看完預熱視頻後再讀API)git
Puppeteer官方文檔:https://github.com/GoogleChrome/puppeteergithub
預熱視頻:https://www.youtube.com/watch?v=IvaJ5n5xFqU 以及 https://www.youtube.com/watch?v=ARt3zDHSsd4typescript
在線編寫測試網站:https://try-puppeteer.appspot.com/npm
puppeteer社區:https://slack.com/api
安裝puppeteer瀏覽器
cnpm i puppeteer
安裝puppeteer-coreapp
cnpm i puppeteer-core
1. puppeteer.launch([options])框架
//Puppeteer 模塊提供了一種啓動 Chromium 實例的方法。 下面就是使用 Puppeteer 進行自動化的一個典型示例: const puppeteer = require('puppeteer'); puppeteer.launch().then(async browser => { const page = await browser.newPage(); await page.goto('https://www.google.com'); // 其餘操做... await browser.close(); });
2.Browser less
當 Puppeteer 鏈接到一個 Chromium 實例的時候會經過 puppeteer.launch 或 puppeteer.connect 建立一個 Browser 對象。
//下面是使用 Browser 建立 Page 的例子 const puppeteer = require('puppeteer'); puppeteer.launch().then(async browser => { // 存儲節點以便能從新鏈接到 Chromium const browserWSEndpoint = browser.wsEndpoint(); // 從 Chromium 斷開和 puppeteer 的鏈接 browser.disconnect(); // 使用節點來從新創建鏈接 const browser2 = await puppeteer.connect({browserWSEndpoint}); // 關閉 Chromium await browser2.close(); });
//一個斷開鏈接和重連到 Browser 的例子: const puppeteer = require('puppeteer'); puppeteer.launch().then(async browser => { // 存儲節點以便能從新鏈接到 Chromium const browserWSEndpoint = browser.wsEndpoint(); // 從 Chromium 斷開和 puppeteer 的鏈接 browser.disconnect(); // 使用節點來從新創建鏈接 const browser2 = await puppeteer.connect({browserWSEndpoint}); // 關閉 Chromium await browser2.close(); });
3.Page
Page 提供操做一個 tab 頁或者 extension background page 的方法。一個 Browser 實例能夠有多個 Page 實例。
下面的例子建立一個 Page 實例,導航到一個 url ,而後保存截圖:
const puppeteer = require('puppeteer'); puppeteer.launch().then(async browser => { const page = await browser.newPage(); await page.goto('https://example.com'); await page.screenshot({path: 'screenshot.png'}); await browser.close(); });
4.Keyboard
Keyboard 提供一個接口來管理虛擬鍵盤. 高級接口爲 keyboard.type, 其接收原始字符, 而後在你的頁面上生成對應的 keydown, keypress/input, 和 keyup 事件.
爲了更精細的控制(虛擬鍵盤), 你可使用 keyboard.down, keyboard.up 和 keyboard.sendCharacter 來手動觸發事件, 就好像這些事件是由真實的鍵盤生成的.
//持續按下 Shift 來選擇一些字符串而且刪除的例子:
await page.keyboard.type('Hello World!'); await page.keyboard.press('ArrowLeft'); await page.keyboard.down('Shift'); for (let i = 0; i < ' World'.length; i++) await page.keyboard.press('ArrowLeft'); await page.keyboard.up('Shift'); await page.keyboard.press('Backspace'); // 結果字符串最終爲 'Hello!'
//按下A的例子 await page.keyboard.down('Shift'); await page.keyboard.press('KeyA'); await page.keyboard.up('Shift');
5. Mouse
Mouse 類在相對於視口左上角的主框架 CSS 像素中運行。
每一個 page 對象都有它本身的 Mouse 對象,使用見 page.mouse。
// 使用 ‘page.mouse’ 追蹤 100x100 的矩形。 await page.mouse.move(0, 0); await page.mouse.down(); await page.mouse.move(0, 100); await page.mouse.move(100, 100); await page.mouse.move(100, 0); await page.mouse.move(0, 0); await page.mouse.up();
更多方法和屬性請閱讀官網
源碼:跳轉到百度首頁
const puppeteer=require('puppeteer'); (async ()=>{ const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}}); const page= await browser.newPage(); await page.goto("https://www.baidu.com"); })();
效果:
代碼:在百度中搜索
const puppeteer=require('puppeteer'); (async ()=>{ const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}}); const page= await browser.newPage(); await page.goto("https://www.baidu.com"); //跳轉頁面 const input_text= await page.$("#kw"); //獲取百度首頁的搜索框。page.$()用來查找元素 await input_text.type("Hello Word!"); //type()輸入內容 const btn_click=await page.$("#su"); //獲取百度首頁的搜索按鈕。 await btn_click.click(); //點擊搜索按鈕。 })();
效果:
源碼:獲取百度詞條的值
const puppeteer=require('puppeteer'); (async ()=>{ const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}}); const page= await browser.newPage(); await page.goto("https://www.baidu.com"); //跳轉頁面 const input_text= await page.$("#kw"); //獲取百度首頁的搜索框。page.$()用來查找元素 await input_text.type("Hello Word!"); //type()輸入內容 const btn_click=await page.$("#su"); //獲取百度首頁的搜索按鈕。 await btn_click.click(); //點擊搜索按鈕。 await page.waitFor('div#content_left > .result-op.c-container.xpath-log',{visible:true});//因爲獲取元素是異步操做,須要等待該元素加載出來 let resultText= await page.$eval('div#content_left > .result-op.c-container.xpath-log',ele=>{return ele.innerHTML})//獲取元素並返回元素下的innerHTML。 .$eval表示獲取單個元素 console.log("result is ",resultText);//在控制檯打印出值。 })();
演示:
代碼:在百度首頁上傳圖片
const puppeteer=require('puppeteer'); (async ()=>{ const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}}); const page = await browser.newPage(); await page.goto('https://www.baidu.com'); await page.waitForSelector('span.soutu-btn'); //等待選擇元素出現 const soutuBtn=await page.$('span.soutu-btn'); //獲取圖片按鈕 await soutuBtn.click(); //點擊展開圖片按鈕 await page.waitForSelector('input.upload-pic'); //等待上傳按鈕出現 const uploadPic=await page.$('input.upload-pic'); //獲取上傳按鈕 await uploadPic.uploadFile('C:\\Capture.PNG'); //上傳圖片。注意:路徑要用雙斜槓。 })();
演示:
代碼:打印京東上的手機信息
const puppeteer = require('puppeteer'); (async()=>{ const browser=await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}}); const page =await browser.newPage(); await page.goto('https://www.jd.com'); await page.waitFor("#key"); const inputText=await page.$("#key"); await inputText.type("手機"); await page.keyboard.press('Enter'); await page.waitForSelector("ul.gl-warp > li"); const items=await page.$$eval("ul.gl-warp > li",eles=>eles.map(ele=>ele.innerText)); console.log("手機列表=",items); })();
演示:
iframe跟page的用法相似。使用前須要切換到iframe裏面並用src定位要操做的iframe。操做可參考:https://github.com/GoogleChrome/puppeteer/blob/v1.20.0/docs/api.md#class-frame
代碼:自動在阿里雲的註冊iframe裏面填寫註冊信息
const puppeteer = require('puppeteer'); (async()=>{ const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}}); const page = await browser.newPage(); await page.goto("https://account.aliyun.com/register/register.html"); //切換iframe const frame= await page.frames().find(frame=>frame.url().includes("https://passport.aliyun.com"));//切換iframe,並找到對應src的iframe await frame.waitFor("input#nick"); //等待輸入框加載完成 const nick =await frame.$("input#nick");//獲取輸入框 await nick.type("測試用戶");//輸入數據 })();
演示:
代碼:自動操做阿里雲驗證滑動模塊
const puppeteer = require('puppeteer'); (async()=>{ const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800},ignoreDefaultArgs:["--enable-automation"]});//在有些頁面可能須要將automation提示去掉。使用ignoreDefaultArgs:["--enable-automation"]參數 const page = await browser.newPage(); await page.goto("https://account.aliyun.com/register/register.html"); //切換iframe const frame= await page.frames().find(frame=>frame.url().includes("https://passport.aliyun.com"));//切換iframe,並找到對應src的iframe await frame.waitForSelector("span#nc_1_n1z");//等待滑動塊加載完成 const span =await frame.$("span#nc_1_n1z"); //獲取滑動元素 const spanInfo=await span.boundingBox(); //獲取滑動塊的信息,包括位置(x,y)以及寬和高 //console.log(spanInfo); await frame.waitForSelector("div#nc_1_n1t"); //等待包裹滑塊的div加載完成 const outDiv=await frame.$("div#nc_1_n1t"); //獲取包裹滑塊的div const outDivInfo=await outDiv.boundingBox(); //獲取包裹滑塊的div的信息,包括位置(x,y)以及寬和高 //console.log(outDivInfo); await page.mouse.move(spanInfo.x,spanInfo.y);//將鼠標移動到滑塊位置 await page.mouse.down(); //將鼠標按下(默認是左鍵) for(var i=0;i<outDivInfo.width;i++){ page.mouse.move(spanInfo.x+i,spanInfo.y);//讓鼠標向左移動outDivInfo的寬度距離 } await page.mouse.up(); //將鼠標鬆開 })();
演示:
代碼:這個實戰彷佛翻車了,被微博檢測到自動化而被要求輸入驗證碼
const puppeteer = require('puppeteer'); const config=require('./config'); //爲了保護個人我的隱私,因此我把帳號和密碼保存在了config文件中 (async ()=>{ // console.log(config.username); // console.log(config.password); const browser = await puppeteer.launch({ headless:false, defaultViewport:{width:1280,height:800}, ignoreDefaultArgs:["--enable-automation"],//移除自動化,防止頁面生成驗證碼 slowMo:200,//輸入延遲時間 args:['--window-size:1280,800'],//調整窗口大小 }); const page= await browser.newPage(); await page.goto('https://news.baidu.com/',{waitUntil:"networkidle2"});//第一個參數是要前往的地址url,第二個參數是保證頁面所有加載 await page.waitForSelector('#imgTitle>a>strong'); const newsText=await page.$eval("#imgTitle>a>strong",ele=>ele.innerText);//匹配第一個元素 // console.log(newsText); await page.goto('https://weibo.com',{waitUntil:"networkidle2"}); await page.waitFor(5*1000);//防止被檢測 await page.reload();//防止被檢測 await page.waitForSelector('#loginname'); //等待帳號輸入框加載完成 const inputText=await page.$('#loginname'); //獲取帳號輸入框元素 await inputText.click(); //防止被檢測,具體狀況具體分析 await inputText.type(config.username); await page.waitForSelector('input[name="password"]'); //等待密碼輸入框加載完成 const inputPwd=await page.$('input[name="password"]'); //獲取密碼輸入框 await inputPwd.click();//防止被檢測,具體狀況具體分析 await inputPwd.type(config.password); await page.waitForSelector('a[action-type="btn_submit"]'); //等待確認按鈕加載 const submit=await page.$('a[action-type="btn_submit"]'); //獲取確認按鈕 await submit.click(); //點擊 })();
演示:翻車啦!!!
代碼演示:https://github.com/GoogleChrome/puppeteer/blob/v1.20.0/docs/api.md#class-keyboard
主要用到api:browser.target();
文檔:https://github.com/GoogleChrome/puppeteer/blob/v1.20.0/docs/api.md#class-browser
主要用到的api:Dialog處理彈出的內容
文檔:https://github.com/GoogleChrome/puppeteer/blob/v1.20.0/docs/api.md#class-dialog
主要用到的方法:page.evaluate(()=>{ 在這裏面能夠寫任意JS代碼 })
文檔:https://github.com/GoogleChrome/puppeteer/blob/v1.20.0/docs/api.md#class-page
待完善
源碼:
const puppeteer =require('puppeteer');//引入puppeteer (async()=>{//使用自執行函數 const browser = await puppeteer.launch();//生成browser實例 const page = await browser.newPage();//生成一個頁面 await page.goto('https://cn.aliyun.com/');//前往頁面 console.log(await page.content());//打印頁面信息(源碼) await page.screenshot({//截圖 path: 'ali.png', fullPage: true }); await browser.close();//關閉browser實例 })();
效果圖: