7

使用puppeteer生成pdf与截图 - lwlcode

 2 years ago
source link: https://www.cnblogs.com/lwlblog/p/16636857.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

之前写过一篇 vue cli2 使用 wkhtmltopdf 踩坑指南,由于wkhtmltopdf对vue的支持并不友好,而且不支持css3,经过调研最终选择puppeteer,坑少,比较靠谱。

一、准备工作

  1. puppeteer中文文档: https://zhaoqize.github.io/puppeteer-api-zh_CN/#/
  2. node版本必须在10.18.1+以上
  3. 新建pdf.js
  4. 安装puppeteernpm install puppeteer(这里用的是15.0.1版本,测试没问题)
  5. 需要生成pdf的html页面需要添加打印样式(不添加会导致背景色无法显示等问题)
    html { -webkit-print-color-adjust: exact;}
  6. cd到pdf.js所在的文件夹执行node pdf.js

二、常用案例

这里直接提供一些常用的生成pdf案例,比较简单,直接复制就能用

1. 通过设置token下载pdf的最简单使用方式

直接执行 node pdf.js 即可

pdf.js

const puppeteer = require('puppeteer')const token = 'kjjkheyJzdWIiOiIxMDAwMDAwMDAwMDAxMjM0' async function printPDF() { const browser = await puppeteer.launch({ headless: true }) const page = await browser.newPage() await page.setExtraHTTPHeaders({'uniedu-sso-token': token}) await page.goto('https://test.web.moedu.net/aital-class-review-h5/#/reportDetail', {waitUntil: 'networkidle0'}) await page.pdf({ path: 'test.pdf', format: 'A4'}) await browser.close()} printPDF()

2. 通过html字符串生成pdf

pdf.js

const puppeteer = require('puppeteer')const html = `<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />· <title>Document</title> <style> html { line-height: 1.15; -webkit-print-color-adjust: exact; } body { margin: 0; font-family: "Times New Roman",'宋体'; font-weight: 400; } </style> </head> <body> <div>页面Dom</div> </body></html>` async function printPDF() { const browser = await puppeteer.launch({ headless: true }) const page = await browser.newPage() await page.setContent(html, {waitUntil: 'networkidle0'}) await page.pdf({ path: 'test.pdf', format: 'A4'}) await browser.close()} printPDF()

3. 简单封装node命令的形式并通过引入html文件生成pdf

首先需要安装 npm install minimist

这里的A3自定义了宽高,puppeteer也有自己默认的A3尺寸,具体详见官方文档page.pdf([options])

生成test.pdf可通过执行该命令 node pdf.js --format=A3 --htmlPath=./index.html --pdfPath=./test.pdf

const puppeteer = require('puppeteer')const fs = require('fs')const args = require('minimist')(process.argv.slice(2))const format = args['format']const htmlPath = args['htmlPath']const pdfPath = args['pdfPath'] const pdfParams = { 'A3': { path: pdfPath, width: '420mm', height: '297mm', margin: { right: '0.1cm', } }, 'A4': { path: pdfPath, format: 'A4' }} async function printPDF(format = 'A4') {// 如果需要部署在服务端尽量加上这行参数 const browser = await puppeteer.launch({args: ['--no-sandbox', '--disable-setuid-sandbox'], headless: true}) const page = await browser.newPage() const htmlContent = fs.readFileSync(htmlPath, 'utf-8') await page.setContent(htmlContent, { waitUntil: 'networkidle0' }) await page.pdf(pdfParams[format]) await browser.close()} printPDF(format)

4. 循环异步批量下载pdf

这里browser只需要打开一次就可以了,只需要每次跳转新页面下载pdf,这样可以不用频繁的开启关闭无头浏览器

const puppeteer = require('puppeteer')const tokens = require('./tokens.json') async function printPDF(page, token, index) { console.log(`第${index + 1}份正在打印……`) await page.goto('https://baidu.com', {waitUntil: 'networkidle0'}); await page.pdf({ path: `./pdf/node${index + 1}.pdf`, format: 'A4'}) console.log(`第${index + 1}份已经完成`)} async function printAll() { console.log(`一共${tokens.length}份,正在打印中……`) const browser = await puppeteer.launch({ headless: true }) const page = await browser.newPage() await page.setExtraHTTPHeaders({'uniedu-sso-token': token}) for(let i = 0; i < tokens.length; i ++){ await printPDF(page, tokens[i], i) } await browser.close()} printAll()

如果需要自动生成文件夹归类,可以用node的fs.existsSync和fs.mkdirSync方法,先判断有没有这个文件夹,没有则创建

if(!fs.existsSync('dirName')) { fs.mkdirSync('dirName')}

5. puppeteer生成截图

这里直接给出最近简单封装的node命令形式的代码作为参考,大部分参数可以参考官方文档

唯一值得说的一个参数是fitContent,这个是我自己加的,可以用于局部的截图,需要html的标签内含有screenshot这个id,说白了就是需要截图的元素用<div id="screenshot"></div>包裹起来

const puppeteer = require('puppeteer')const fs = require('fs')const args = require('minimist')(process.argv.slice(2)) const clip = {}args.clipX && (clip.x = Number(args.clipX))args.clipY && (clip.y = Number(args.clipY))args.clipW && (clip.width = Number(args.clipW))args.clipH && (clip.height = Number(args.clipH)) let params = {}args.imgPath && (params.path = args.imgPath)args.type && (params.type = args.type)args.quality && (params.quality = Number(args.quality))args.fullPage && (params.fullPage = args.fullPage === 'true')args.omitBackground && (params.omitBackground = args.omitBackground === 'true')args.encoding && (params.encoding = args.encoding)Object.keys(clip).length !== 0 && (params.clip = clip) async function printImg() { const browser = await puppeteer.launch({args: ['--no-sandbox', '--disable-setuid-sandbox'], headless: true}) const page = await browser.newPage() const htmlContent = fs.readFileSync(args.htmlPath, 'utf-8') await page.setContent(htmlContent, { waitUntil: 'networkidle0' }) const range = await page.$('#screenshot') const clip = await range.boundingBox() const result = args.fitContent === 'true' ? { ...params, clip } : params await page.screenshot(result) await browser.close()} printImg() /* 参数说明: htmlPath: html文件路径 imgPath: 截图保存路径。截图图片类型将从文件扩展名推断出来。如果是相对路径,则从当前路径解析。如果没有指定路径,图片将不会保存到硬盘 type: 指定截图类型, 可以是 jpeg 或者 png。默认 'png'. quality: 图片质量, 可选值 0-100. png 类型不适用。 fullPage: 如果设置为true,则对完整的页面(需要滚动的部分也包含在内)。默认是false clipX: 指定裁剪区域相对于左上角(0, 0)的x坐标 clipY: 指定裁剪区域相对于左上角(0, 0)的y坐标 clipW: 指定裁剪区域的宽度 clipH: 指定裁剪区域的高度 omitBackground: 隐藏默认的白色背景,背景透明。默认不透明 encoding: 图像的编码可以是 base64 或 binary。 默认为“二进制”。 fitContent: 设为true,则只对id="screenshot"包裹的内容区域截图*/ // node 命令示例// node puppeteer_img.js --htmlPath=./index.html --imgPath=aa.png --fullPage=true --fitContent=true

6. 调用本地chrome

puppeteer默认会安装一个最新版本的chromiue,也可以调起本地的chrome,这时候需要使用puppeteer-core

安装npm i puppeteer-core carlo

const puppeteer = require('puppeteer-core');//find_chrome模块来源于GoogleChromeLabs的Carlo,可以查看本机安装Chrome目录 const findChrome = require('./node_modules/carlo/lib/find_chrome.js') ;(async () => { let findChromePath = await findChrome({}) let executablePath = findChromePath.executablePath; console.log(executablePath) const browser = await puppeteer.launch({ executablePath, headless: false }) const page = await browser.newPage() await page.goto('https://www.baidu.com/') // await browser.close()})()

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK