安装 安装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
来源:oschina
链接:https://my.oschina.net/hongjiang/blog/4473217