Puppeteer是一个Node库,我们可以用它来控制一个无头的Chrome实例。我们基本上是在使用Chrome,但以编程方式使用JavaScript。
使用它,我们可以
- 刮取网页
- 自动提交表单
- 执行任何类型的浏览器自动化
- 跟踪页面加载性能
- 创建单页应用程序的服务器端渲染版本
- 制作屏幕截图
- 创建自动化测试
- 从网页中生成PDF
它是由谷歌建立的。它本身并没有解锁任何新东西,但它抽象了许多我们必须处理的琐碎细节,而没有使用它。
简而言之,它使事情变得非常简单。
由于它在初始化时启动了一个新的Chrome实例,所以它可能不是性能最好的。但这是用Chrome浏览器进行自动化测试的最精确的方法,因为它使用的是引擎盖下的实际浏览器。
准确地说,它使用Chromium,即Chrome的开源部分,这主要意味着你没有谷歌授权的专有编解码器,不能开源(MP3、AAC、H.264......),也没有与谷歌服务的整合,如崩溃报告、谷歌更新等,但从编程的角度看,它应该与Chrome100%相似(除了媒体播放,如前所述)。
安装Puppeteer
首先,在你的项目中使用
在你的项目中安装它。
这将下载并捆绑最新版本的 Chromium。
你可以选择通过安装puppeteer-core 来使 puppeteer 运行你已经安装的 Chrome 的本地安装,这在某些特殊情况下很有用(见puppeteer vs puppeteer-core)。通常情况下,你只需使用puppeteer 。
使用 Puppeteer
在一个Node.js文件中,要求它。
const puppeteer = require('puppeteer');
然后我们可以使用launch() 方法来创建一个浏览器实例。
(async () => {
const browser = await puppeteer.launch()
})()
我们也可以这样写。
puppeteer.launch().then(async browser => {
//...
})
你可以向puppeteer.launch() 传递一个带有选项的对象。最常见的是
puppeteer.launch({ headless:false })
在Puppeteer执行操作时显示Chrome。它可以很好地看到正在发生的事情并进行调试。
我们使用await ,所以我们必须把这个方法调用包装在一个异步函数中,并立即调用。
接下来我们可以在browser 对象上使用newPage() 方法来获得page 对象。
(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
})()
接下来我们调用page 对象上的goto() 方法来加载该页面。
(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto('https://website.com')
})()
我们也可以使用承诺,而不是async/await,但使用后者可以使事情更容易阅读。
(() => {
puppeteer.launch().then(browser => {
browser.newPage().then(page => {
page.goto('https://website.com').then(() => {
//...
})
})
})
})()
获取页面内容
一旦我们有了一个加载了URL的页面,我们就可以通过调用page 的evaluate() 方法来获取页面内容。
(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto('https://website.com')
const result = await page.evaluate(() => {
//...
})
})()
这个方法需要一个回调函数,在这里我们可以添加所需的代码来检索我们需要的页面元素。我们返回一个新对象,这将是我们调用evaluate() 方法的结果。
我们可以使用page.$() 方法来访问文档中的Selectors API方法querySelector() ,而page.$$() 作为querySelectorAll() 的别名。
一旦我们完成了计算,我们就可以调用close() 方法,在browser 。
页面方法
我们在上面看到了我们通过调用browser.newPage() 得到的page 对象,我们对它调用了goto() 和evaluate() 方法。
所有的方法都会返回一个承诺,所以它们通常会在前面加上await 关键字。
让我们看看我们将调用的一些最常见的方法。你可以在Puppeteer的文档中看到完整的列表。
page.$()
允许访问页面上的Selectors API方法querySelector()
page.$$()
允许访问页面上的Selectors API方法querySelectorAll()
page.$eval()
接受2个或多个参数。第一个是一个选择器,第二个是一个函数。如果有更多的参数,这些参数将作为附加参数传递给函数。
它在页面上运行querySelectorAll() ,使用第一个参数作为选择器,然后它使用该参数作为函数的第一个参数。
const innerTextOfButton = await page.$eval('button#submit', el => el.innerText)
click()
在作为参数传递的元素上执行一个鼠标点击事件
await page.click('button#submit')
我们可以用一个选项对象传递一个额外的参数。
button可以设置为 (默认)、 或leftrightmiddleclickCount是一个数字,默认为1,设置元素应该被点击多少次delay是两次点击之间的毫秒数。默认值是0
content()
获取一个页面的HTML源代码
const source = await page.content()
emulate()
模拟一个设备。它将用户代理设置为一个特定的设备,并相应地设置视口。
支持的设备列表可以在这个文件中找到。
下面是模拟iPhone X的方法。
iPhone X
const puppeteer = require('puppeteer');
const device = require('puppeteer/DeviceDescriptors')['iPhone X'];
puppeteer.launch().then(async browser => {
const page = await browser.newPage()
await page.emulate(device)
//do stuff
await browser.close()
})
evaluate()
在页面上下文中评估一个函数。在这个函数里面我们可以访问document 对象,所以我们可以调用任何DOM API。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto('https://flaviocopes.com')
const result = await page.evaluate(() => {
return document.querySelectorAll('.footer-tags a').length
})
console.log(result)
})()
我们在这里调用的任何东西都在页面上下文中执行,所以如果我们运行console.log() ,我们不会在Node.js上下文中看到结果,因为那是在无头浏览器中执行的。
我们可以在这里计算值并返回一个JavaScript对象,但如果我们想返回一个DOM元素并在Node.js上下文中访问它,我们必须使用一个不同的方法,evaluateHandle() 。如果我们从evaluate()返回一个DOM元素,我们只会得到一个空对象。
evaluateHandle()
与evaluate()类似,但如果我们返回一个DOM元素,我们将得到适当的对象,而不是一个空对象。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto('https://flaviocopes.com')
const result = await page.evaluateHandle(() => {
return document.querySelectorAll('.footer-tags a')
})
console.log(result)
})()
exposeFunction()
这个方法允许你在浏览器上下文中添加一个新函数,在Node.js上下文中执行。
这意味着我们可以添加一个函数,在浏览器内运行Node.js代码。
这个例子在浏览器上下文中添加了一个test()函数,从文件系统中读取一个 "app.js "文件,其路径与脚本相对。
const puppeteer = require('puppeteer');
const fs = require('fs');
(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto('https://flaviocopes.com')
await page.exposeFunction('test', () => {
const loadData = (path) => {
try {
return fs.readFileSync(path, 'utf8')
} catch (err) {
console.error(err)
return false
}
}
return loadData('app.js')
})
const result = await page.evaluate(() => {
return test()
})
console.log(result)
})()
focus()
聚焦于作为参数传递的选择器
await page.focus('input#name')
goBack()
在页面导航历史中回溯
goForward()
在页面导航历史中往前走
goto()
打开一个新的页面。
await page.goto('https://flaviocopes.com')
你可以传递一个对象作为第二个参数,并带有选项。waitUntil 选项,如果传递的是networkidle2 值,则会等待,直到导航完成。
await page.goto('https://flaviocopes.com', {waitUntil: 'networkidle2'})
hover()
在作为参数传递的选择器上做一次鼠标移动
await page.hover('input#name')
pdf()
从一个页面生成一个PDF。你可以
await page.pdf({ path: 'file.pdf })
你可以向这个方法传递许多选项,来设置生成的PDF的细节。参见官方文档。
reload()
重新加载一个页面
screenshot()
获取页面的PNG截图,并保存到使用path 选择的文件名。
await page.screenshot({path: 'screenshot.png'})
select()
选择由作为参数传递的选择器识别的DOM元素
await page.select('input#name')
setContent()
你可以设置一个页面的内容,而不是打开一个现有的网页。
对用现有的HTML以编程方式生成PDF或屏幕截图很有用。
const html = '<h1>Hello!</h1>'
await page.setContent(html)
await page.pdf({path: 'hello.pdf'})
await page.screenshot({path: 'screenshot.png'})
setViewPort()
默认情况下,视口是800x600px。如果你想有一个不同的视口,也许是为了截图,可以调用setViewport ,传递一个带有width 和height 属性的对象。
await page.setViewport({ width: 1280, height: 800 })
title()
获取页面标题
type()
变成一个识别表单元素的选择器的类型
await page.type('input#name', 'Flavio')
delay 选项允许像现实世界的用户一样模拟打字,在每个字符之间添加延迟。
await page.type('input#name', 'Flavio', {delay: 100})
url()
获取页面URL
viewport()
获取页面视口
waitFor()
等待特定的事情发生。具有以下快捷功能。
waitForFunctionwaitForNavigationwaitForRequestwaitForResponsewaitForSelectorwaitForXPath
例子。
await page.waitFor(waitForNameToBeFilled)
const waitForNameToBeFilled = () => page.$('input#name').value != ''
页面命名空间
一个页面对象可以让你访问几个不同的对象。
每一个对象都能解锁大量的新功能。
keyboard 和 ,很可能是你在尝试自动化时使用最多的。mouse
例如,这是你如何触发对一个元素的输入(之前应该已经被选中)。
await page.keyboard.type('hello!')
其他的键盘方法有
keyboard.down()发送一个下键事件keyboard.press()发送一个下键和一个上键的事件(模拟正常的按键类型)。主要用于修改键(shift, ctrl, cmd)。keyboard.sendCharacter()发送一个按键事件keyboard.type()发送一个降键、按键和上键事件keyboard.up()发送一个上键事件
所有这些都接收美国键盘布局文件中定义的键盘按键代码:https://github.com/GoogleChrome/puppeteer/blob/master/lib/USKeyboardLayout.js。正常的字符和数字按原样输入,而特殊的键有一个特殊的代码来定义它们。
mouse 提供4种方法。
mouse.click()模拟一次点击: 和 事件mousedownmouseupmouse.down()模拟一个 事件mousedownmouse.move()移动到不同的坐标mouse.up()模拟一个 事件mouseup