之前寫過一篇 vue cli2 使用 wkhtmltopdf 踩坑指南,由於wkhtmltopdf對vue的支援並不友好,而且不支援css3,經過調研最終選擇puppeteer,坑少,比較靠譜。
npm install puppeteer
(這裡用的是15.0.1版本,測試沒問題)html {
-webkit-print-color-adjust: exact;
}
這裡直接提供一些常用的生成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()
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()
首先需要安裝 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)
這裡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')
}
這裡直接給出最近簡單封裝的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
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()
})()