puppeteer
puppeteer是一種谷歌開發的Headless Chrome,因為puppeteer的出現,業內許多自動化測試庫停止維護,比如PhantomJS,Selenium IDE for Firefox 。
puppeteer是干啥用的?
官方給了一些功能:
- 頁面生成pdf
- 爬spa/ssr類的網站
- 自動提交表單,模擬用戶操作,ui測試等等
- 提供自動化測試環境
- 分析網頁性能問題,基於chrome timeline
其實對於這么一個瀏覽器,我們能做的還有很多,比如前端監控,定期查詢頁面異常。這種思想產生的page-monitor。主要的功能其實就是基於它是一個瀏覽器,它可以模擬用戶輸入。能做什么依賴你的想象。
用code介紹一下puppeteer
頁面生成pdf
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://open.toutiao.com', {waitUntil: 'networkidle2'});
await page.pdf({path: 'hn.pdf', format: 'A4'});
await browser.close();
})();
puppeteer是基於node v6.4.0,但是await/async的語法需要node v7.6.0以上才支持。
可以npm i puppeteer
然后在命令行看一下效果。
代碼都是api沒有什么可以講的。需要說的一點就是open.toutiao.com下面的文章內容都是異步接口請求,puppeteer是怎么獲取內容的?
page.goto的配置項waitUntil:networkidle2, 等待一直到500ms內的請求數不超過2個。其實不保證准確獲得內容,那把等待時間寫長一點就可以了。
await page.waitFor(2000);
調試
- puppeteer並不是只有headless模式,打開puppeteer的ui界面:
puppeteer.launch({headless: false)
,再放慢puppeteer執行的動作puppeteer.launch({headless: false, slowMo: 250})
,就可以輕松調試。 - ‘打call?’
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
事件監聽輕松打出頁面的log。
爬蟲
這里爬一下頭條的新聞標題:
(async () => {
const browser = await puppeteer.launch({headless: false, slowMo: 250});
const page = (await browser.pages())[0];
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
await page.goto('https://open.toutiao.com');
await page.evaluate(() => console.log(`url is ${location.href}`));
const newsTitle = await page.evaluate((sel) => {
const $els = document.querySelectorAll(sel);
return Array.from($els).map((v) => {
console.log(v.innerText); // 會被page.on 'console' 監聽到
return v.innerText
})
}, 'section h3');
console.log(newsTitle) // 可以處理新聞標題。
await page.screenshot({path: 'toutiao.png'}); // 屏幕快照
await browser.close();
})();
模擬用戶操作
這個功能用途挺多的,比如自動登陸,e2e測試,刷贊,搶票什么的,當然如果能跳過驗證碼的話。
github 登陸
模擬輸入用戶名和密碼。
await page.goto('https://github.com/login');
await page.click('#login_field');
await page.type('username');
await page.click('#password');
await page.type('password');
await page.click('#login > form > div.auth-form-body.mt-3 > input.btn.btn-primary.btn-block');
await page.waitForNavigation();
puppetter提供了page.focus,page.click,page.type,page.$eval(獲取dom屬性)等等api,鼠標位置,按鍵按下,tap,頁面跳轉眾多用戶可操作的api,都可以通過程序來模擬。
對這種模擬登陸,puppeteer還貼心的提供了這種api - -!
page.type('#mytextarea', 'World', {delay: 100}); // Types slower, like a user
ui測試
之前分享過的testcafe,跟puppeteer的api非常像,testcafe是一個自動化測試框架,他與puppeteer不同的一點就是他集成了mocha斷言庫。
puppeteer和testcafe都提供了一套自動化測試的環境。puppeteer做e2e的測試需要自己選一個斷言庫,不過無傷大雅。
請求攔截/模擬請求
puppeteer比testcafe好的一點就是支持請求攔截,記得當初用testcafe測試請求是否被發出用了很多黑科技,提過issue。。
const puppeteer = require('puppeteer');
puppeteer.launch({headless: false, slowMo: 250}).then(async browser => {
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
page.on('request', interceptedRequest => {
if (interceptedRequest.url().endsWith('.png') || interceptedRequest.url().endsWith('.jpg'))
interceptedRequest.abort();
else
interceptedRequest.continue();
});
await page.goto('https://open.toutiao.com');
// await browser.close();
});
提供了request,response事件,可以攔截請求,首先需要打開這個開關await page.setRequestInterception(true);
。
這里的例子就是停掉所有的png和jpg請求。
攔截能做的東西有很多,比如一些爬蟲可以通過攔截請求捕獲一些數據,來處理一些東西。
修改環境
puppeteer可以通過page.setViewport,page.setUserAgent來修改訪問的環境。
await page.setViewport({
width: 1920,
height: 1080
});
await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36');
puppeteer/DeviceDescriptors
還給我們封裝好了一些環境,比如:
const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhone = devices['iPhone 6'];
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
await page.emulate(iPhone); // emulate的配置有Viewport,UserAgent等等。之前的setUserAgent等方法是它的語法糖。
await page.goto('https://www.google.com');
// other actions...
await browser.close();
});
性能測試
可以生成一個trace.json的文件,供chrome控制台解析,await page.metrics()
還可以給出一些性能測試的數據。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.tracing.start({path: 'trace.json'})
await page.goto('https://open.toutiao.com')
await page.tracing.stop()
const metrics = await page.metrics()
console.log(metrics)
await browser.close();
})();
// output
{ Timestamp: 27888.820538,
Documents: 2,
Frames: 1,
JSEventListeners: 58,
Nodes: 171,
LayoutCount: 20,
RecalcStyleCount: 26,
LayoutDuration: 0.042335,
RecalcStyleDuration: 0.010091,
ScriptDuration: 0.124838,
TaskDuration: 0.000039,
JSHeapUsedSize: 6388448,
JSHeapTotalSize: 10334208 }