Node库Puppeteer的介绍

127 阅读4分钟

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的页面,我们就可以通过调用pageevaluate() 方法来获取页面内容

(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 可以设置为 (默认)、 或left right middle
  • clickCount 是一个数字,默认为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 ,传递一个带有widthheight 属性的对象。

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()

等待特定的事情发生。具有以下快捷功能。

  • waitForFunction
  • waitForNavigation
  • waitForRequest
  • waitForResponse
  • waitForSelector
  • waitForXPath

例子。

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() 模拟一次点击: 和 事件mousedown mouseup
  • mouse.down() 模拟一个 事件mousedown
  • mouse.move() 移动到不同的坐标
  • mouse.up() 模拟一个 事件mouseup