puppeteer 的奇技淫巧
- 作者:Bougie
- 创建于:2019-05-26
- 更新于:2023-03-09
# 前言
Puppeteer(中文翻译"木偶") 是 Google Chrome 团队官方的无界面(Headless)Chrome 工具,它是一个 Node 库,提供了一个高级的 API 来控制 DevTools 协议上的无头版 Chrome
# 开启一个页面
;(async function() {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto('https://bougieL.github.io')
}
# 获取一个页面的所有链接
由于 puppeteer 是无头浏览器,所以对于非服务端渲染的页面,也是能获取页面所有 link 的,我们可以很轻松的获取一个页面所有链接
async function getUrlsFromPage(page: puppeteer.Page) {
return await Promise.all(
(await page.$$('a')).map(async (el) => {
return await (await el.getProperty('href')).jsonValue()
})
)
})
# 获取一个页面所有资源文件的链接(爬虫必备)
利用性能分析工具获取所有资源链接
async function getStaticUrlsFromPage(page: puppeteer.Page) {
return await page.evaluate(() => {
return performance
.getEntries()
.filter((e) => e.entryType === 'resource')
.map((e) => e.name)
})
}
# 生成站点地图
page 开多了内存会爆,利用 async.waterfall
进行流程控制
const urls = []
;(async function sitemap(oUrls) {
const fns = oUrls.map((url, idx) => {
return async (prev = []) => {
await page.goto(url, { waitUntil: 'domcontentloaded' })
const urls = await getUrlsFromPage(page)
return [...prev, ...urls]
}
})
async.waterfall(fns, (err, res: string[]) => {
res = (res || []).filter((url) => {
return !urls.includes(url)
})
urls = [...urls, ...oUrls, ...res]
if (res.length === 0) {
browser.close()
callback(urls)
} else {
sitemap(res)
}
})
})([domain])
# 预渲染 SPA 页面
如果你的 SPA 应用页面较少,不需从后端抓取数据生成动态页面,那么可以用 puppeteer 进行页面的预渲染。适用于部署在 github pages 上的页面。
yarn add @bougiel/puppeteer-prerenderer -D
参考 bougie-design/scripts/prerender.js (opens new window).
自动获取路由,自动渲染页面
const path = require('path')
const {
createSPAServer,
renderUrlsToString,
getRelativePathFromUrl,
getUrlsFromSite,
writeFile
} = require('@bougiel/puppeteer-prerenderer')
const dist = path.resolve(__dirname, '../.docz')
const port = 4200
const server = createSPAServer({
dist,
port,
base: '/bougie-design',
onCreated: render
})
function render() {
getUrlsFromSite(`http://localhost:${port}/bougie-design`, (urls) => {
renderUrlsToString({
urls,
onItemRendered(content, url) {
const rp = getRelativePathFromUrl(url)
const p = path.join(dist, rp)
writeFile(p, content)
},
onFinished() {
server.close()
process.exit(0)
}
})
})
}