塞班岛贵宾会用于Chrome/Chromium的基于节点的库:Puppeteer-Web前端之家

浏览器开发人员工具为网站和Web应用程序的底层提供了许多惊人的选择。这些功能可以通过第三方工具进一步增强和自动化。在本文中,我们将研究Puppeteer,这是一个用于Chrome
/ Chromium的基于节点的库。

时间: 2019-03-31阅读: 421标签: pdf

一个Node库,它提供了高级API来通过DevTools协议控制Chrome或Chromium。Puppeteer默认情况下无头运行,但可以配置为运行完整的Chrome或Chromium。

在本文中,我将展示如何使用 Node.js、Puppeteer、headless Chrome 和
Docker 从样式复杂的 React 页面生成 PDF 文档。

Puppeteer由Google
Chrome背后的团队开发,因此您可以肯定它将得到很好的维护。它使我们能够通过一个简单易用的API通过JavaScript以编程方式在Chromium浏览器上执行常见操作。

背景:几个月前,一个客户要求我们开发一个功能,用户可以得到 PDF 格式的
React
页面内容。该页面基本上是患者病例的报告和数据可视化结果,其中包含许多
SVG。另外还有一些特殊的请求来操纵布局,并对 HTML
元素进行一些重新排列。因此与原始的 React 页面相比,PDF
中应该有不同的样式和额外的内容。

使用Puppeteer,您可以:

由于这个任务比用简单的 CSS
规则解决要复杂得多,所以我们先探讨了可能的实现方法。我们找到了 3
个主要解决方案。这篇博文将指导你了解它们的可能性并最终实施。

生成网站截图,包括SVG和Canvas

在客户端还是服务器端生成?

使用标准DOM API访问网页并提取信息

在客户端和服务器端都可以生成PDF文件。但是让后端处理它可能更有意义,因为你并不想耗尽用户浏览器可以提供的所有资源。

生成预渲染的内容-即服务器端渲染

即便如此,我仍然会展示这两种方法的解决方案。

像Cypress一样自动化UI测试

方案1:从 DOM 制作屏幕截图

Puppeteer并没有Selenium,PhantomJS之类的东西做任何新的事情,但是它提供了一个简单易用的API,并提供了很好的抽象性,因此我们不必担心问题的实质。细节处理。

乍一看,这个解决方案似乎是最简单的,事实证明的确是这样,但它有其自身的局限性。如果你没有特殊需求,例如在
PDF 中选择文本或对文本进行搜索,那么这就是一种简单易用的方法。

它也得到了积极的维护,因此Chromium支持ECMAScript的所有新功能。

此方法简单明了:从页面创建屏幕截图,并把它放到 PDF
文件中。非常直截了当。我们可以使用两个包来实现:

先决条件

Html2canvas,根据 DOM 生成截图jsPdf,一个生成PDF的库

对于本教程,您需要JavaScript,ES6 +和Node.js的基础知识。

开始编码:

您还必须已经安装了最新版本的Node.js的。

npm install html2canvas jspdf

yarn在本教程中,我们将一直使用。如果yarn尚未安装,请从此处安装。

import html2canvas from 'html2canvas'import jsPdf from 'jspdf' function printPDF () { const domElement = document.getElementById('your-id') html2canvas(domElement, { onclone: (document) = { document.getElementById('print-button').style.visibility = 'hidden'}}) .then((canvas) = { const img = canvas.toDataURL('image/png') const pdf = new jsPdf() pdf.addImage(imgData, 'JPEG', 0, 0, width, height) pdf.save('your-filename.pdf')})

为了确保我们在同一页面上,这些是本教程中使用的版本:

就这样!

Node 12.12.0

请注意html2canvas的onclone方法。当你在截图之前需要操纵
DOM(例如隐藏打印按钮)时,它是非常方便的。我看到过很多使用这个包的项目。但不幸的是,这不是我们想要的,因为我们需要在后端完成对
PDF 的创建工作。

yarn 1.19.1

方案2:只使用 PDF 库

puppeteer 2.0.0

NPM上有几个库,如
jsPDF(如上所述)或PDFKit。他们的问题是,如果我想使用这些库,我将不得不重新调整页面结构。这肯定会损害可维护性,因为我需要将所有后续更改应用到
PDF 模板和 React 页面中。

安装

请看下面的代码。你需要亲自手动创建 PDF 文档。你需要遍历 DOM
并找出每个元素并将其转换为 PDF
格式,这是一项繁琐的工作。必须找到一个更简单的方法。

要在项目中使用Puppeteer,请在终端中运行以下命令:

doc = new PDFDocumentdoc.pipe fs.createWriteStream('output.pdf')doc.font('fonts/PalatinoBold.ttf') .fontSize(25) .text('Some text with an embedded font!', 100, 100) doc.image('path/to/image.png', { fit: [250, 300], align: 'center', valign: 'center'}); doc.addPage() .fontSize(25) .text('Here is some vector graphics...', 100, 100) doc.end()
$yarnaddpuppeteer

这段代码段来自 PDFKit 文档。但是如果你的目标是直接生成一个 PDF
文件,而不是对一个已经存在的(并且不断变化的)HTML
页面进行转换,它还是很有用的。

注意:安装Puppeteer时,它会下载保证可与该API一起使用的Chromium的最新版本(〜170MB
macOS,〜282MB Linux,〜280MB Win)。要跳过下载,请参阅环境变量。

最终方案3:基于 Node.js 的 Puppeteer 和 Headless Chrome

如果您不需要下载Chromium,则可以安装:puppeteer-core

什么是Puppeteer?其文档中写道:

$yarnaddpuppeteer-core

Puppeteer 是一个 Node 库,它提供了一个高级 API 来控制 DevTools 协议上的
Chrome 或 Chromium。 Puppeteer 默认以 headless 模式运行 Chrome 或
Chromium,但其也可以被配置为完整的(non-headless)模式运行。

puppeteer-core塞班岛贵宾会,旨在成为Puppeteer的轻量级版本,用于启动现有的浏览器安装或连接到远程浏览器。确保您安装的puppeteer-core版本与您打算连接的浏览器兼容。

它本质上是一个可以从 Node.js
运行的浏览器。如果你读过它的文档,其中首先提到的就是你可以用 Puppeteer
生成页面的截图和PDF。优秀!这正是我们想要的。

注意:仅从1.7.0版发布。puppeteer-core

先用npmi i puppeteer安装 Puppeteer,并实现我们的功能。

用法

const puppeteer = require('puppeteer') async function printPDF() { const browser = await puppeteer.launch({ headless: true }); const page = await browser.newPage(); await page.goto('', {waitUntil: 'networkidle0'}); const pdf = await page.pdf({ format: 'A4' }); await browser.close(); return pdf})

Puppeteer至少需要Node v6.4.0,但我们将使用async / await,仅在Node
v7.6.0或更高版本中受支持,因此请确保将Node.js更新到最新版本以获取所有好处。

这是一个简单的功能,可导航到 URL 并生成站点的 PD F文件。

让我们深入研究一些使用Puppeteer的实际示例。在本教程中,我们将是:

首先,我们启动浏览器(仅在 headless 模式下支持 PDF
生成),然后打开新页面,设置视口,并导航到提供的URL。

使用Puppeteer生成Unsplash的屏幕截图

设置waitUntil:’networkidle0’选项意味着当至少500毫秒没有网络连接时,Puppeteer
会认为导航已完成。 (可以从API docs获取更多信息。)

使用Puppeteer创建黑客新闻PDF

之后,我们将 PDF 保存为变量,关闭浏览器并返回 PDF。

使用Puppeteer登录Facebook

注意:page.pdf方法接收options对象,你可以使用 ‘path’
选项将文件保存到磁盘。如果未提供路径,则 PDF
将不会被保存到磁盘,而是会得到缓冲区。(稍后我将讨论如何处理它。)

1.使用Puppeteer生成Unsplash的屏幕截图

如果需要先登录才能从受保护的页面生成
PDF,首先你要导航到登录页面,检查表单元素的 ID
或名称,填写它们,然后提交表单:

使用Puppeteer做到这一点真的很容易。继续,在项目的根目录中创建一个文件。然后粘贴以下代码:screenshot.js

await page.type('#email', process.env.PDF_USER)await page.type('#password', process.env.PDF_PASSWORD)await page.click('#submit')
constpuppeteer=requireconstmain=async()=>{constbrowser=awaitpuppeteer.launch()constpage=awaitbrowser.newPage()awaitpage.goto('https://unsplash.com')awaitpage.screenshot({path:'unsplash.png'})awaitbrowser.close

要始终将登录凭据保存在环境变量中,不要硬编码!

首先,我们需要puppeteer包装。然后,我们launch在其上调用初始化实例的方法。此方法是异步的,因为它返回一个Promise。因此,我们await为其获取browser实例。

样式控制

然后我们调用newPage它并转到Unsplash并对其进行截图,并将截图另存为。unsplash.png

Puppeteer 也有这种样式操作的解决方案。你可以在生成 PDF
之前插入样式标记,Puppeteer 将生成具有已修改样式的文件。

现在继续输入以下内容在终端中运行以上代码:

awaitpage.addStyleTag({content:'.nav { display: none} .navbar { border: 0px} #print-button {display: none}'})
$nodescreenshot

将文件发送到客户端并保存

现在,在5-10秒后,您将在项目中看到一个包含Unsplash屏幕截图的文件。请注意,视口设置为800px
x
600px,因为Puppeteer将此视口设置为初始页面尺寸,该尺寸定义了屏幕截图的尺寸。可以使用Page.setViewport()自定义页面大小。unsplash.png

好的,现在你已经在后端生成了一个 PDF 文件。接下来做什么?

让我们将视口更改为1920px x 1080px。在goto方法之前插入以下代码:

如上所述,如果你不把文件保存到磁盘,将会得到一个缓冲区。你只需要把含有适当内容类型的缓冲区发送到前端即可。

awaitpage.setViewport({width:1920,height:1080,deviceScaleFactor:1,})
printPDF.then(pdf = { res.set({ 'Content-Type': 'application/pdf', 'Content-Length': pdf.length }) res.send(pdf)

现在,继续前进,也可以更改文件名以在像这样的方法:unsplash.png``unsplash2.png``screenshot

现在,你只需在浏览器向服务器发送请求即可得到生成的 PDF。

awaitpage.screenshot({path:'unsplash2.png'})
function getPDF() { return axios.get(`${API_URL}/your-pdf-endpoint`, { responseType: 'arraybuffer', headers: { 'Accept': 'application/pdf' } })

现在,整个文件应如下所示:screenshot.js

一旦发送了请求,缓冲区的内容就应该开始下载了。最后一步是将缓冲区数据转换为
PDF 文件。

constpuppeteer=requireconstmain=async()=>{constbrowser=awaitpuppeteer.launch()constpage=awaitbrowser.newPage()awaitpage.setViewport({width:1920,height:1080,deviceScaleFactor:1,})awaitpage.goto('https://unsplash.com')awaitpage.screenshot({path:'unsplash2.png'})awaitbrowser.close
savePDF = () = { this.openModal(‘Loading…’) // open modal return getPDF() // API call .then((response) = { const blob = new Blob([response.data], {type: 'application/pdf'}) const link = document.createElement('a') link.href = window.URL.createObjectURL(blob) link.download = `your-file-name.pdf` link.click() this.closeModal() // close modal }) .catch(err = /** error handling **/) }button onClick={this.savePDF}Save as PDF/button

2.使用Puppeteer创建黑客新闻PDF

就这样!如果单击“保存”按钮,那么浏览器将会保存 PDF。

现在创建一个名为的文件,并将以下代码粘贴到其中:pdf.js

在 Docker 中使用 Puppeteer

constpuppeteer=requireconstmain=async()=>{constbrowser=awaitpuppeteer.launch()constpage=awaitbrowser.newPage()awaitpage.goto('https://news.ycombinator.com',{waitUntil:'networkidle2'})awaitpage.pdf({path:'hn.pdf',format:'A4'})awaitbrowser.close

我认为这是实施中最棘手的部分 —— 所以让我帮你节省几个小时的百度时间。

我们仅从screenshot代码中更改了两行。

官方文档指出“在 Docker 中使用 headless Chrome
并使其运行起来可能会非常棘手”
。官方文档有疑难解答部分,你可以找到有关用
Docker 安装 puppeteer 的所有必要信息。

首先,我们将网址替换为Hacker News,然后添加了networkidle2

如果你在 Alpine 镜像上安装
Puppeteer,请确保在看到页面的这一部分时再向下滚动一点。否则你可能会忽略一个事实:你无法运行最新的
Puppeteer 版本,并且你还需要用一个标记禁用 shm :

awaitpage.goto('https://news.ycombinator.com',{waitUntil:'networkidle2'})
const browser = await puppeteer.launch({ headless: true, args: ['--disable-dev-shm-usage']});

networkidle2对于进行长时间轮询或任何其他附带活动的页面非常有用,并且当至少两个网络连接持续至少500毫秒时,认为导航已完成。

否则,Puppeteer 子进程可能会在正常启动之前耗尽内存。

然后,我们调用了pdf创建PDf的方法并将其调用,并将其格式化为大小:hn.pdf``A4

方案 3 + 1:CSS 打印规则

awaitpage.pdf({path:'hn.pdf',format:'A4'})

可能有人认为从开发人员的角度来看,简单地使用 CSS 打印规则很容易。没有
NPM 模块,只有纯 CSS。但是在跨浏览器兼容性方面,它的表现如何呢?

而已。现在,我们可以运行该文件来生成Hacker
News的PDF。让我们继续在终端中运行以下命令:

在选择 CSS
打印规则时,你必须在每个浏览器中测试结果,以确保它提供的布局是相同的,并且它不是100%能做到这一点。

$nodepdf

例如,在给定元素后面插入一个 break-after
并不是一个多么高深的技术,但是你可能会惊讶的发现要在 Firefox
中使用它需要使用变通方法。

这将在项目的根目录中生成A4大小的PDF文件。hn.pdf

除非你是一位经验丰富的 CSS
大师,在创建可打印页面方面有很多的经验,否则这可能会非常耗时。

3.使用Puppeteer登录到Facebook

如果你可以使打印样式表保持简单,打印规则是很好用的。

使用以下代码创建一个名为的新文件:signin.js

让我们来看一个例子吧。

constpuppeteer=requireconstSECRET_EMAIL='example@gmail.com'constSECRET_PASSWORD='secretpass123'constmain=async()=>{constbrowser=awaitpuppeteer.launchconstpage=awaitbrowser.newPage()awaitpage.goto('https://facebook.com',{waitUntil:'networkidle2'})awaitpage.waitForSelector('#login_form')awaitpage.type('input#email',SECRET_EMAIL)awaitpage.type('input#pass',SECRET_PASSWORD)awaitpage.click('#loginbutton')//awaitbrowser.close
@media print { .print-button { display: none; } .content div { break-after: always; }}

我们创建了两个变量SECRET_EMAILSECRET_PASSWORD,应将其替换为您的Facebook电子邮件和密码。

上面的 CSS
隐藏了打印按钮,并在每个div之后插入一个分页符,其中包含content类。有一篇很棒的文章总结了你可以用打印规则做什么,以及它们有什么问题,包括浏览器兼容性。

然后,我们launch的浏览器,并设置headless模式,false推出了完整版的Chromium浏览器的。

考虑到所有因素,如果你想从不那么复杂的页面生成
PDF,CSS打印规则非常有效。

然后,我们转到Facebook,等待所有内容加载完毕。

总结

在Facebook上,#login_form可以通过DevTools访问选择器。该选择器包含登录表单,因此我们使用waitForSelector方法来等待它。

让我们快速回顾前面介绍的方案,以便从 HTML 页面生成 PDF 文件:

然后,我们需要输入我们的emailpassword,所以我们抓住选择input#email,并input#pass从DevTools,并通过我们的SECRET_EMAILSECRET_PASSWORD

从 DOM
产生截图
:当你需要从页面创建快照时(例如创建缩略图)可能很有用,但是当你需要处理大量数据时就会有些捉襟见肘。只用
PDF 库
:如果你打算从头开始以编程方式创建 PDF
文件,这是一个完美的解决方案。否则,你需要同时维护 HTML 和 PDF
模板,这绝对是一个禁忌。Puppeteer:尽管在 Docker
上工作相对困难,但它为我们的实现提供了最好的结果,而且编写代码也是最简单的。CSS打印规则:如果你的用户受过足够的教育,知道如何把页面内容打印到文件,并且你的页面相对简单,那么它可能是最轻松的解决方案。正如你在我们的案例中所看到的,事实并非如此。

之后,我们单击#loginbutton以登录到Facebook。

打印快乐!

最后一行被注释掉,使我们看到了打字的全过程email,并password和点击登录按钮。

翻译:疯狂的技术宅原文:

在终端中键入以下内容,继续运行代码:

$nodesignin

这将启动整个Chromium浏览器,然后登录Facebook。

结论

在本教程中,我们创建了一个项目,该项目创建指定视口内任何给定页面的屏幕截图。我们还建立了一个项目,可以在其中创建任何网站的PDF。然后,我们以编程方式设法登录了Facebook。

Puppeteer最近发布了版本2,它是一个很好的软件,可以通过简单易用的API自动执行琐碎的任务。

您可以在其官方网站上了解有关Puppeteer的更多信息。这些文档非常好,有大量的示例,并且所有文档都有据可查。

现在,使用Puppeteer可以自动执行您日常生活中无聊的任务。

发表评论

电子邮件地址不会被公开。 必填项已用*标注