初尝Puppeteer
首先肯定是照搬它的项目简介了哈哈哈哈
- 利用网页生成截图以及pdf
- 爬取SPA生成预渲染页面内容(我们说的ssr)
- 可以从网站爬取内容
- 自动化表单提交、UI测试、键盘输入等等
- 创建一个最新的自动化测试环境(chrome),可以直接在这个上面测试用例运行最新的JavaScript和浏览器功能。
- 捕获网站的时间线跟踪,以帮助诊断性能问题。
本文主要是通过一些小例子,把它的介绍基本都跑上一遍。嘻嘻
install
yarn add puppeteer
# or "npm i puppeteer"
安装puppeteer时,它会下载最新版本的Chromium,以保证与api协同工作,如果要跳过下载的话可以参考环境变量的设置
1.利用网页生成截图以及pdf
// PNG
const puppeteer = require('puppeteer');
// 引入puppeteer
(async ()=>{
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(`https://example.com`);
await page.screenshot({
path: 'example.png'
});
await browser.close();
})();
puppeteer默认设置一个page大小为800px*600px,而这个大小也将定义了截图的大小。
当然这个页面大小我们可以通过Page.setViewport()来设置。
// PDF
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle2'});
await page.pdf({path: 'hn.pdf', format: 'A4'});
await browser.close();
})();
// 当然了.pdf方法提供了更多参数可以使用
2、爬取SPA生成预渲染页面
首先当然是简单地写一个spa页面,其实原理很简单,这样做的ssr,也是以前经常做spa页面ssr的原理,但是以前是用sel较多,利用一个可执行js的内核,进行渲染页面后,把整个页面内容返回,也就是解析执行还是在后端进行,后端做多一层转发
现在演示demo 可以直接用parcel+vue,组装速度要快不少~~
服务端的demo
const puppeteer = require('puppeteer');
const Koa = require('koa')
const app = new Koa()
async function getSpaContent(ctx, next) {
if(/\.html/.test(ctx.request.url)){
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(`http://localhost:1234${ctx.request.url}`)
console.log(`visiting http://localhost:1234${ctx.request.url}`)
await page.content().then((v)=>{ //此处返回的是promise 简单地获取即可
ctx.body = v;
})
await browser.close();
}else{
ctx.body = {};
}
await next();
}
app.use(getSpaContent);
let len = process.argv.length;
let port = `3000`;
for(let i =0;i{
console.log(`listening in port with ${port}`);
})
3、网站爬取内容
由于它的特性,可以完全模拟浏览器行为,从而进行获取信息。在这种情况下,浏览器对简单爬虫的防护可谓是形同虚设。我们可以很简单得模拟浏览器模拟用户操作。
const puppeteer = require('puppeteer');
const nextLink = `a[rel*="next"]`;
const ARTICLE_ITEM = `.repo-list-item`;
const TITLE_SELECTOR = `div > h3 > a`;
const STAR_SELECTOR = `div > .muted-link`;
const CONTENT_SELECTOR = `div > div > p`;
const mainLink = `https://github.com/search?p=1&q=javascript&type=Repositories&utf8=%E2%9C%93`;
const data = [];
process.setMaxListeners(0);
(async() => {
const browser = await puppeteer.launch({
headless:true //要看演示可以使用false
});
const page = await browser.newPage();
let val = {};
await page.setViewport({
width:980,
height:980
})
try {
await page.goto(mainLink);
val = await page.evaluate((nextLink) => {
return document.querySelector(nextLink); //主要是审查页面元素 防止进入深渊进入死循环
}, nextLink);
while (val !== null && !!val) {
await page.evaluate((ARTICLE_ITEM, TITLE_SELECTOR, STAR_SELECTOR, CONTENT_SELECTOR) => {
function searchElement(parent = null) {
function getDataWithNull(element, attr, defaultValue) {
if ((element !== null) && (element instanceof HTMLElement)) {
return element[attr];
} else {
return defaultValue;
}
}
if (parent === null) {
parent = document;
}
return {
title: getDataWithNull(parent.querySelector(TITLE_SELECTOR), 'innerText', ''),
star: getDataWithNull(parent.querySelector(STAR_SELECTOR), 'innerText', ''),
content: getDataWithNull(parent.querySelector(CONTENT_SELECTOR), 'innerText', '')
}
}
return Array.from(document.querySelectorAll(ARTICLE_ITEM)).map((val) => {
return searchElement(val)
})
}, ARTICLE_ITEM, TITLE_SELECTOR, STAR_SELECTOR, CONTENT_SELECTOR).then((v) => {
data.push(v);
return v;
})
await page.click(nextLink)
await page.waitForNavigation({timeout:500}).then(()=>{},async (a) => {
val = await page.evaluate((nextLink) => {
return document.querySelector(nextLink);
}, nextLink);
})
await page.screenshot({
path: 'demo3.png',// 拍个照证明我们确实是因为调入深渊了
fullPage: true
})
}
} catch (e) {
// 速度太快会进入深渊。这里只是演示所以直接点。
console.log(`共爬取 ${data.length*10}`)
} finally {
await browser.close();
}
})();
// 逻辑很简单。大概不用解释。。
4、简单的测试以及搜索自动提交
// 测试
// 使用mocha测试
const assert = require('assert'); //使用assert断言库
const puppeteer = require('puppeteer');
const WEBSITE_TITLE = 'kangkangblog – Mr.kangblog';
const MY_GITHUB_LINK = 'https://github.com/ZWkang';
const FIRST_ITEM_TEXT = '首页';
let browser;
let page;
before(async ()=>{
browser = await puppeteer.launch({
headless:true
})
page = await browser.newPage()
await page.goto('https://ls-l.cn')
})
describe('check my website',()=>{
it('i need a title man!!',async ()=>{
const titleValue = await page.title().then((title_value)=>{
return title_value
})
assert.equal(titleValue,WEBSITE_TITLE)
}).timeout(10000);
it('menu frist item',async ()=>{
await page.waitForSelector('#site-navigation')
const titleItem = await page.evaluate(()=>{
return document.querySelectorAll('#site-navigation ul > li')[0].innerText;
})
assert.equal(titleItem,FIRST_ITEM_TEXT);
}).timeout(10000);
it(`the website will have my github link`,async()=>{
const my_github_link = await page.evaluate(()=>{
return document.querySelector('.call-to-action-button').href
})
assert.equal(my_github_link,MY_GITHUB_LINK)
}).timeout(10000);
})
after(async ()=>{
await browser.close()
})
// 表单提交
// 键盘输入
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false //要看演示可以使用false
});
const page = await browser.newPage();
await page.setViewport({// 设置viewport尺寸
width:1280,
height:980
})
await page.goto('https://segmentfault.com/')
await page.waitForSelector('#searchBox')
await page.click('#searchBox')
await page.type('javascript',{delay:100})
await page.click('.btn-link')
await page.waitForSelector('.search-result')
await page.waitFor(8000).then(async ()=>{
await page.screenshot({
path: 'keyboardTest.png',// 拍个照
fullPage: true
})
})
await browser.close()
})();
5、网站的时间线跟踪
就是将proformance的数据获取导出json
要使用生成的json也简单,在浏览器proformance导入即可
学会利用timeline proformance也是很重要的一件事呀!~~
const puppeteer = require('puppeteer');
(async ()=>{
const browser = await puppeteer.launch({
headless:true
})
const page = await browser.newPage()
await page.tracing.start({path: 'trace.json'});
await page.goto('https://ls-l.cn');
await page.tracing.stop()
await page.close()
await browser.close()
})()
示例代码地址
本文示例代码
总结
测试可以给我们更了解自己的代码,在update之后更快地得到反馈。
puppeteer给前端带来了新的意义~一部分携带着统一。
在未来可以看到会有基于puppeteer封装的二次工具的出现~保持学习,迎接未来的挑战吧~
欢迎有不同观点的合理讨论。嘻嘻