Puppeteer 出自于 GoogleChrome 团队,是一个可以用来模拟 Chrome 浏览器各种操作行为的 nodejs 库,基于谷歌的开发工具协议。
它可以用来模拟你在浏览器中大多数常见操作,比如:
- 生成页面的截图或者是PDF
- 抓取单页应用和生成预渲染的内容
- 抓取网站内容
- 自动提交表单、UI测试、模拟键盘输入等
- 创新一个最新的、自动化的测试环境,可以在最新版本 Chrome 浏览器上运行你的测试用例
- 捕获你的站点的时间轴,帮助你找出需要优化的问题
Puppeteer 运行依赖的 nodejs 版本最低是6.4.0,但是由于示例中使用了async/await的特性,所以我建议你使用7.6.0以及更高的版本。
安装 Puppeteer
yarn add puppeteer
//或者
npm install puppeteer
截屏示例
第一个示例:自动跳转到 https://example.com 并生成一张截图:
const puppeteer = require('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 设置的默认可视区域大小是800*600像素。上面示例中的网站页面小于这个尺寸,可以完整的截取出来。但是,你换成http://www.jd.com就不行了,所以,我们得使用page.setViewport()方法来重新定义可视区域的大小。
//设置截取页面的可视区域大小
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.goto('https://www.jd.com');
await page.setViewport({
width: 1200,
height: 800
});
await page.screenshot({path: 'jd.png'});
await browser.close();
})();
运行查看截图,发现只是完整的截取了第一屏,后面几屏的怎么办?page.screenshot()方法提供了一个fullPage参数,用来设置截取整个页面。
//截取整个京东商城页面,但是因为有懒加载,所以不能截取到完整的内容
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.goto('https://www.jd.com');
await page.setViewport({
width: 1200,
height: 800
});
await page.screenshot({
path: 'jd.png',
fullPage: true
});
await browser.close();
})();
截取的确实是整个网站页面,但是有些楼层使用了懒加载机制,导致这些楼层就没有截取出来。解决办法就是能够让页面自动从顶部滚动到底部之后,再去进行截取,所以我们需要自己编写一个autoScroll()方法。
//自动滚动截取整个京东商城页面
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.goto('https://www.jd.com');
await page.setViewport({
width: 1200,
height: 800
});
await autoScroll(page);
await page.screenshot({
path: 'jd.png',
fullPage: true
});
await browser.close();
})();
async function autoScroll(page){
await page.evaluate(async () => {
await new Promise((resolve, reject) => {
var totalHeight = 0;
var distance = 100;
var timer = setInterval(() => {
var scrollHeight = document.body.scrollHeight;
window.scrollBy(0, distance);
totalHeight += distance;
if(totalHeight >= scrollHeight){
clearInterval(timer);
resolve();
}
}, 100);
});
});
}
重点解释一下autoScroll()方法的实现。totalHeight用来记录页面的当前高度,初始值为0。distance用来表示每次向下滚动的距离,这里为100像素。接着使用了一个定时器,每隔100毫秒向下滚动distance设定的距离,然后累加到totalHeight,直到它大于等于页面的实际高度document.body.scrollHeight之后,才会清除定时器,并将Promise对象的状态置为resolve()。
页面滚动完成之后,后面的处理跟上面一样了,直接执行截屏操作就可以了。
模拟用户输入与鼠标事件
上面已经说过,puppeteer 还可以模拟键盘的输入操作和鼠标单击事件,基于这些我们可以自然想到可以用它模拟表单提交操作。
编写了一个简单的 html 页面来模拟表单:
<!DOCTYPE html>
<html>
<head>
<title>index</title>
<style type="text/css">
input, button{
font-size: 20px;
}
</style>
<script type="text/javascript">
function submit(){
alert('提交成功!');
}
</script>
</head>
<body>
文本框:<input type="text" name="" id="text"> <button id="button" onclick="submit()">提交</button>
</body>
</html>

在文本框中自动输入一串数字,然后自动点击提交按钮。我们用到了 puppeteer 的 page.type和page.click方法,前者用于模拟输入,后者用于模拟单击操作。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.goto("localhost:80/html/index.html", {
waitUntil: "networkidle"
});
await page.type('#text', '123456789', {
delay: 100
});
await page.waitFor(500);
await page.click('#button', {
delay: 500
})
await browser.close();
})();

puppeteer.launch()方法在之前的版本中有一个devtool: true参数,可在页面中自动打开 Chrome 的开发者工具。可是后面的版本,不知道什么原因给去掉了。如果你现在还有此需求,可以这样写:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false,
args: ['--auto-open-devtools-for-tabs']
});
const page = await browser.newPage();
await page.goto("http://www.jd.com", {
waitUntil: "networkidle"
});
await browser.close();
})();
可以使用page.emulate()方法来模拟各种移动设备。最重要的是userAgent参数,因为服务器一般都是根据这个参数值来决定显示的页面类型的。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.goto("http://www.jd.com", {
waitUntil: "networkidle"
});
await page.emulate({
viewport: {
width: 375,
height: 667,
isMobile: true
},
userAgent: '"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"'
});
})();
此外,还有page.hover()用来模拟 mouseover 的操作;page.reload()用来模拟刷新操作;page.title()用来获取网页标题。这些大家都可以自己去使用挖掘一下。
过滤页面中的元素
有时候我打开一个网页可能只是想分析它里面的超级链接,并不想让页面加载图片,这可以大大加快页面的访问速度。所以,你可以给页面绑定一个request的事件,可以通过它的回调函数参数获取到当前页面加载的每一个请求,并加以处理。
我们这里就可以根据它的url()来判断当前请求是图片的话,直接将其abort(),否则continue()即可。
const puppeteer = require('puppeteer');
puppeteer.launch({
headless: false
}).then(async browser => {
const page = await browser.newPage();
await page.setRequestInterception(true);
await page.setViewport({
width: 1200,
height: 800
});
page.on('request', interceptedRequest => {
let url = interceptedRequest.url();
if(url.indexOf('.png') > -1 || url.indexOf('.jpg') > -1)
interceptedRequest.abort();
else
interceptedRequest.continue();
});
await page.goto('https://www.jd.com');
await autoScroll(page);
// await browser.close();
});
创建隐私模式
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false
});
// Create a new incognito browser context
const context = await browser.createIncognitoBrowserContext();
// Create a new page inside context.
const page = await context.newPage();
// ... do stuff with page ...
await page.goto('https://example.com');
// Dispose context once it's no longer needed.
await context.close();
})();
官方文档
Puppeteer 出自于 GoogleChrome 团队,是一个可以用来模拟 Chrome 浏览器各种操作行为的 nodejs 库,基于谷歌的开发工具协议。
它可以用来模拟你在浏览器中大多数常见操作,比如:
Puppeteer 运行依赖的 nodejs 版本最低是
6.4.0,但是由于示例中使用了async/await的特性,所以我建议你使用7.6.0以及更高的版本。安装 Puppeteer
截屏示例
第一个示例:自动跳转到 https://example.com 并生成一张截图:
Puppeteer 设置的默认可视区域大小是
800*600像素。上面示例中的网站页面小于这个尺寸,可以完整的截取出来。但是,你换成http://www.jd.com就不行了,所以,我们得使用page.setViewport()方法来重新定义可视区域的大小。运行查看截图,发现只是完整的截取了第一屏,后面几屏的怎么办?
page.screenshot()方法提供了一个fullPage参数,用来设置截取整个页面。截取的确实是整个网站页面,但是有些楼层使用了懒加载机制,导致这些楼层就没有截取出来。解决办法就是能够让页面自动从顶部滚动到底部之后,再去进行截取,所以我们需要自己编写一个
autoScroll()方法。重点解释一下
autoScroll()方法的实现。totalHeight用来记录页面的当前高度,初始值为0。distance用来表示每次向下滚动的距离,这里为100像素。接着使用了一个定时器,每隔100毫秒向下滚动distance设定的距离,然后累加到totalHeight,直到它大于等于页面的实际高度document.body.scrollHeight之后,才会清除定时器,并将Promise对象的状态置为resolve()。页面滚动完成之后,后面的处理跟上面一样了,直接执行截屏操作就可以了。
模拟用户输入与鼠标事件
上面已经说过,puppeteer 还可以模拟键盘的输入操作和鼠标单击事件,基于这些我们可以自然想到可以用它模拟表单提交操作。
编写了一个简单的 html 页面来模拟表单:
在文本框中自动输入一串数字,然后自动点击提交按钮。我们用到了 puppeteer 的
page.type和page.click方法,前者用于模拟输入,后者用于模拟单击操作。puppeteer.launch()方法在之前的版本中有一个devtool: true参数,可在页面中自动打开 Chrome 的开发者工具。可是后面的版本,不知道什么原因给去掉了。如果你现在还有此需求,可以这样写:可以使用
page.emulate()方法来模拟各种移动设备。最重要的是userAgent参数,因为服务器一般都是根据这个参数值来决定显示的页面类型的。此外,还有
page.hover()用来模拟 mouseover 的操作;page.reload()用来模拟刷新操作;page.title()用来获取网页标题。这些大家都可以自己去使用挖掘一下。过滤页面中的元素
有时候我打开一个网页可能只是想分析它里面的超级链接,并不想让页面加载图片,这可以大大加快页面的访问速度。所以,你可以给页面绑定一个
request的事件,可以通过它的回调函数参数获取到当前页面加载的每一个请求,并加以处理。我们这里就可以根据它的
url()来判断当前请求是图片的话,直接将其abort(),否则continue()即可。创建隐私模式
官方文档