使用docker puppeteer api 接口返回
安装 安装docker,使用模拟手机访问
mkdir /docker/puppeteer-renderer/src
目录中的代码有 index.js, renderer.js,wait-for-animations.js ,代码依次是
"use strict"
const express = require("express")
const qs = require("qs")
const { URL } = require("url")
const contentDisposition = require("content-disposition")
const createRenderer = require("./renderer")
const port = process.env.PORT || 3000
const app = express()
let renderer = null
// Configure.
app.set("query parser", s => qs.parse(s, { allowDots: true }))
app.disable("x-powered-by")
// Render url.
app.use(async (req, res, next) => {
let { url, type, filename, ...options } = req.query
if (!url) {
return res.status(400).send("Search with url parameter. For eaxample, ?url=http://yourdomain")
}
if (!url.includes("://")) {
url = `http://${url}`
}
try {
switch (type) {
case "pdf":
const urlObj = new URL(url)
if (!filename) {
filename = urlObj.hostname
if (urlObj.pathname !== "/") {
filename = urlObj.pathname.split("/").pop()
if (filename === "") filename = urlObj.pathname.replace(///g, "")
const extDotPosition = filename.lastIndexOf(".")
if (extDotPosition > 0) filename = filename.substring(0, extDotPosition)
}
}
if(!filename.toLowerCase().endsWith(".pdf")) {
filename += ".pdf";
}
const { contentDispositionType, ...pdfOptions } = options
const pdf = await renderer.pdf(url, pdfOptions)
res
.set({
"Content-Type": "application/pdf",
"Content-Length": pdf.length,
"Content-Disposition": contentDisposition(filename, { type: contentDispositionType || "attachment" }),
})
.send(pdf)
break
case "screenshot":
const { screenshotType, buffer } = await renderer.screenshot(url, options)
res
.set({
"Content-Type": `image/${screenshotType}`,
"Content-Length": buffer.length,
})
.send(buffer)
break
default:
//自定义方法
const html = await renderer.renderHtml(url)
res.status(200).send(html)
}
} catch (e) {
next(e)
}
})
// Error page.
app.use((err, req, res, next) => {
console.error(err)
res.status(500).send("Oops, An expected error seems to have occurred.")
})
// Create renderer and start server.
createRenderer({
ignoreHTTPSErrors: !!process.env.IGNORE_HTTPS_ERRORS,
})
.then(createdRenderer => {
renderer = createdRenderer
console.info("Initialized renderer.")
app.listen(port, () => {
console.info(`Listen port on ${port}.`)
})
})
.catch(e => {
console.error("Fail to initialze renderer.", e)
})
// Terminate process
process.on("SIGINT", () => {
process.exit(0)
})
renderer.js
"use strict"
const puppeteer = require("puppeteer")
const waitForAnimations = require("./wait-for-animations")
class Renderer {
constructor(browser) {
this.browser = browser
}
async createPage(url, options = {}) {
const { timeout, waitUntil, credentials, emulateMedia } = options
const page = await this.browser.newPage()
if (emulateMedia) {
await page.emulateMedia(emulateMedia)
}
if (credentials) {
await page.authenticate(credentials)
}
await page.goto(url, {
timeout: Number(timeout) || 30 * 1000,
waitUntil: waitUntil || "networkidle2",
})
return page
}
async render(url, options = {}) {
let page = null
try {
const { timeout, waitUntil, credentials } = options
page = await this.createPage(url, { timeout, waitUntil, credentials })
const html = await page.content()
return html
} finally {
if (page) {
await page.close()
}
}
}
//自定义模拟器手机访问
async renderHtml(url, options = {}) {
try{
let browser = await puppeteer.launch({
// 是否不显示浏览器, 为true则不显示
"args": ["--no-sandbox", "--disable-setuid-sandbox"]
});
// 通过浏览器实例 Browser 对象创建页面 Page 对象
let page = await browser.newPage();
const UA = "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1";
await Promise.all([
page.setUserAgent(UA),
// 允许运行js
page.setJavaScriptEnabled(true),
// 设置页面视口的大小
page.setViewport({width: 1100, height: 1080}),
]);
await page.goto(url);
let content= await page.content();
await browser.close();
return content;
}catch(err){
console.log(err)
}
}
async pdf(url, options = {}) {
let page = null
try {
const { timeout, waitUntil, credentials, emulateMedia, ...extraOptions } = options
page = await this.createPage(url, { timeout, waitUntil, credentials, emulateMedia: emulateMedia || "print" })
const { scale = 1.0, displayHeaderFooter, printBackground, landscape } = extraOptions
const buffer = await page.pdf({
...extraOptions,
scale: Number(scale),
displayHeaderFooter: displayHeaderFooter === "true",
printBackground: printBackground === "true",
landscape: landscape === "true",
})
return buffer
} finally {
if (page) {
await page.close()
}
}
}
async screenshot(url, options = {}) {
let page = null
try {
const { timeout, waitUntil, credentials, ...extraOptions } = options
page = await this.createPage(url, { timeout, waitUntil, credentials })
page.setViewport({
width: Number(extraOptions.width || 800),
height: Number(extraOptions.height || 600),
})
const { fullPage, omitBackground, screenshotType, quality, ...restOptions } = extraOptions
let screenshotOptions = {
...restOptions,
type: screenshotType || "png",
quality:
Number(quality) || (screenshotType === undefined || screenshotType === "png" ? 0 : 100),
fullPage: fullPage === "true",
omitBackground: omitBackground === "true",
}
const animationTimeout = Number(options.animationTimeout || 0)
if (animationTimeout > 0) {
await waitForAnimations(page, screenshotOptions, animationTimeout)
}
const buffer = await page.screenshot(screenshotOptions)
return {
screenshotType,
buffer,
}
} finally {
if (page) {
await page.close()
}
}
}
async close() {
await this.browser.close()
}
}
async function create(options = {}) {
const browser = await puppeteer.launch(
Object.assign({ args: ["--no-sandbox"] }, options)
)
return new Renderer(browser)
}
module.exports = create
wait-for-animations.js
"use strict"
const PNG = require("pngjs").PNG
const pixelmatch = require("pixelmatch")
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
async function waitForAnimations(page, options, timeout = 10000) {
const t0 = new Date().getTime()
let previous = null
while (new Date().getTime() - t0 < timeout) {
const current = PNG.sync.read(await page.screenshot({ ...options, type: "png" }))
if (previous !== null) {
const diff = pixelmatch(previous.data, current.data, null, previous.width, previous.height)
if (diff === 0) {
return true
}
}
previous = current
await sleep(100)
}
return false
}
module.exports = waitForAnimations
//下面是安装
docker run -d –name renderer -v /docker/puppeteer-renderer/src:/app/src -p 8040:3000 zenato/puppeteer-renderer
//下面是访问 http://ip:8040/?url=http://m.xxx.com