本文转载自CSDN博主「夏狗花花」的原创文章,文章地址:https://blog.csdn.net/lychee_xiahua/article/details/119045640
Puppeteer 简介
Puppeteer 是一个Chrome官方团队提供的node库,它可以通过 Puppeteer 的提供的 API 直接控制 Chrome 或 Chromiun。
Puppeteer 可以做什么
1) 生成网页截图或者 PDF
2) 爬取SPA应用,并生成预渲染内容(即“SSR” 服务端渲染)
3) 高级爬虫,可以爬取大量异步渲染内容的网页
4) 模拟键盘输入、表单自动提交、登录网页等
5) 创建一个最新的自动化测试环境,实现 UI 自动化测试
6) 捕获站点的时间线,以便追踪网站、帮助分析网站性能问题
7) 用于测试 Chrome 扩展程序
运行环境
Nodejs 的版本不能低于 v7.6.0,因为需要支持 async, await.
需要最新的 chrome driver(安装 Puppeteer 时会自动下载)
Puppeteer 架构图
Puppeteer API 是分层的,并反映了浏览器的结构。
Puppeteer 使用 DevTools Protocol 与浏览器通信
Browser 实例可以拥有多个浏览器上下文
BrowserContext 实例定义了一个浏览会话,可以拥有多个页面
Page 至少有一个 frame :main frame。可能还有其他由 iframe 创建的 frame 或标签
Frame 至少有一个可执行上下文 - 默认执行上下文 - 在其中执行 frame 的 JavaScript 。一个 Frame 可能有与 extensions 有关联的其他执行上下文
Worker 具有单个执行上下文,便于与 WebWorkers 交互
Puppeteer 初体验 —— 入门 Demo
官方入门 Demo 先感受一下基本用法:
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); //启动一个无头浏览器 const page = await browser.newPage(); //打开一个标签页 await page.goto('https://example.com'); //跳转到指定的页面 await page.screenshot({path: 'example.png'}); // 对页面进行截图 await browser.close(); //关闭浏览器 })();
Puppeteer 模块
Puppeteer 模块提供了一种启动 Chromium 实例的方法。
示例
启动一个浏览器
const puppeteer = require('puppeteer'); puppeteer.launch().then(async browser => { const page = await browser.newPage(); await page.goto('https://www.google.com'); // 其他操作... await browser.close(); });
puppeteer.launch([options])
可配置项(options)
属性 | 类型 | 说明 | 默认值 |
---|---|---|---|
ignoreHTTPSErrors | Boolean | 是否在导航期间忽略 HTTPS 错误 | false |
headless | Boolean | 是否以无头模式运行浏览器 | true(除非 devtools 选项是 true) |
devtools | Boolean | 是否为每个选项卡自动打开DevTools面板 | false |
timeout | Number | 等待浏览器实例启动的最长时间(以毫秒为单位),设置 0 禁用超时 | 30000(毫秒) |
slowMo | Number | 将 Puppeteer 操作减少指定的毫秒数(为了方便调试) | |
※ defaultViewport | Object | 为每个页面设置一个默认视口大小。如果为 null 的话就禁用视图口 | |
dumpio | Boolean | 是否将浏览器进程标准输出和标准错误输入到 process.stdout 和 process.stderr 中 | false |
更多配置项参考官网
※ defaultViewport 可选参数
属性 | 类型 | 说明 | 默认值 |
---|---|---|---|
width | Number | 页面宽度像素 | 800 |
height | Number | 页面高度像素 | 600 |
deviceScaleFactor | Number | 设置设备的缩放(可以认为是 dpr) | 1 |
isMobile | Boolean | 是否在页面中设置了 meta viewport 标签 | false |
hasTouch | Boolean | 指定viewport是否支持触摸事件 | false |
isLandscape | Boolean | 指定视口是否处于横向模式 | false |
puppeteer.connect(options)
这是将 Puppeteer 添加到已有的 Chromium 实例的方法。
可配置项(options)
属性 | 类型 | 说明 | 默认值 |
---|---|---|---|
browserWSEndpoint | String | 一个 浏览器 websocket 端点链接(必填) | |
ignoreHTTPSErrors | Boolean | 是否在导航期间忽略 HTTPS 错误 | false |
defaultViewport* | Object | 为每个页面设置一个默认视口大小。如果为 null 的话就禁用视图口 | |
slowMo | Number | 将 Puppeteer 操作减少指定的毫秒数(为了方便调试) |
Browser 对象
当 Puppeteer 连接到一个 Chromium 实例的时候会通过 puppeteer.launch 或 puppeteer.connect 创建一个 Browser 对象。
示例
示例1:使用 Browser 创建 Page
const puppeteer = require('puppeteer'); puppeteer.launch().then(async browser => { const page = await browser.newPage(); await page.goto('https://example.com'); await browser.close(); });
示例2:断开连接和重连到 Browser
const puppeteer = require('puppeteer'); puppeteer.launch().then(async browser => { // 存储节点以便能重新连接到 Chromium const browserWSEndpoint = browser.wsEndpoint(); // 从 Chromium 断开和 puppeteer 的连接 browser.disconnect(); // 使用节点来重新建立连接 const browser2 = await puppeteer.connect({browserWSEndpoint}); // 关闭 Chromium await browser2.close(); });
Browser Methods
browser.newPage():打开一个新的页面(创建一个新的 Page 对象)
browser.disconnect():断开 Puppeteer 和浏览器的连接,但 Chromium 进程仍然在运行
browser.close() :关闭 Chromium 及其所有页面
browser.wsEndpoint():返回浏览器 websocket 的地址
page 对象
Page 提供操作一个 tab 页或者 extension background page(一个 chrome 拓展程序) 的方法。一个 Browser 实例可以有多个 Page 实例。
示例
示例1:创建一个 Page 实例,导航到一个 url ,然后保存截图
const puppeteer = require('puppeteer'); puppeteer.launch().then(async browser => { const page = await browser.newPage(); await page.goto('https://example.com'); await page.screenshot({path: 'screenshot.png'}); await browser.close(); });
示例2:用 on 或者 once 捕获 Page 事件,用 removeListener 移除事件监听
page.once('load', () => console.log('Page loaded!')); //捕获 page 实例的 load 事件 function logRequest(interceptedRequest) { console.log('A request was made:', interceptedRequest.url()); } page.on('request', logRequest); //监听 request 事件 // 一段时间后... page.removeListener('request', logRequest); //移除request监听
示例3:搜索并抓取图片
1、yarn add request
2、新建index.js
const puppeteer = require('puppeteer'); const request = require("request"); const fs = require('fs'); var scrape = async () => { // 初始化无头浏览器 const browser = await puppeteer.launch({ headless: false, defaultViewport: { width: 1200, height: 1000 } }); // 新建页面 const page = await browser.newPage(); //搜索关键字 let word = '美女' // 跳转到指定页面 await page.goto('https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=index&fr=&hs=0&xthttps=111110&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=' + word); await page.evaluate(function () { //该页面使用懒加载,滑动屏幕让图片加载 window.scrollTo(0,1000) setTimeout(()=>{window.scrollTo(0,2000)},1000) }) await page.waitForTimeout(3000) // 获取节点 const data = await page.evaluate( ()=> { //拿到图片节点 let dom = document.querySelectorAll('#imgid img') let arr = [] for(let i=0;i<dom.length-1;i++){ //去除base64格式 if(!dom[i].src.includes('base64')) arr.push(dom[i].src) } //返回图片链接 return arr }) //关闭浏览器 browser.close() return data } scrape().then((value) => { for(let i = 1; i < value.length-1; i++) { let imgUrl = value[i]; let filename = `${new Date().getTime()}imgage${i}.png`; //在C盘下创建文件夹 fs.mkdir('C:/tempmeinv',(e)=>{ if(!e || (e&&e).code === 'EEXIST'){ if(imgUrl) //保存在这个目录下 request(imgUrl).pipe(fs.createWriteStream("C:/tempmeinv/" + filename)); else console.log('地址为空') } else{ console.log('err') } }) } });
3、cmd 执行 node index.js
Page Events
page.on('load'):当页面的 load 事件被触发时触发
page.on('close'):当页面关闭时触发
page.on('request'):当页面发送一个请求时触发
page.on('response'):当页面的某个请求接收到对应的 response 时触发
page.on('error'):当页面崩溃时触发
page.on('console'):当页面js代码调用了 console 的某个方法时触发
Page Methods
基础类
page.goto(url[, options])导航到指定地址
page.close() 关闭页面
page.setViewport(viewport) 设置页面视口
page.evaluate(pageFunction[, ...args]) 在页面实例上下文中执行方法
page.setRequestInterception(value) 是否启用请求拦截器
cookie 类
page.cookies([...urls])返回指定页面域名的 cookie(若未指定url,返回当前页面域名的 cookie)
page.setCookie(...cookies) 设置 cookie
page.deleteCookie(...cookies) 删除指定 cookie
元素选择、操作类
page.$(selector) document.querySelector
page.$$(selector) document.querySelectorAll
page.type(selector, text[, options]) 模拟键盘输入内容
page.click(selector[, options]) 点击匹配元素
page.tap(selector) 点击匹配元素(通过 page.touchscreen 点击)
page.focus(selector) 使匹配元素获得焦点
page.hover(selector) 使匹配元素获得hover状态
page.select(selector, ...values) 选中匹配的选择器,触发change和input事件
waitFor 系列
page.waitForFunction(pageFunction[, options[, ...args]]):等待函数执行完成
page.waitForNavigation([options]):等待页面跳转完成
page.waitForRequest(urlOrPredicate[, options]):等待匹配的请求
page.waitForResponse(urlOrPredicate[, options]):等待匹配的响应
page.waitForSelector(selector[, options]):等待指定的选择器匹配的元素出现在页面中
page.waitForXPath(xpath[, options]):等待指定的xpath匹配的元素出现在页面中
page.waitForTimeout(milliseconds) :等待的毫秒数 (v10.1.0 新增 )
page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]]) :新版本中已废弃,使用 waitForSelector、waitForFunction、 waitForTimeout 代替
总之很实用类
page.pdf([options]) 生成PDF
page.screenshot([options]) 截图
实战场景
Puppeteer 实战场景 —— 生成 PDF
参考链接
文档类
Puppeteer 官网:https://pptr.dev
Puppeteer 中文文档:https://learnku.com/docs/puppeteer/3.1.0
Puppeteer 中文文档(另一版):http://www.puppeteerjs.com/
经验帖
puppeteer (Nodejs版selenium )快速入门
puppetee 常用API
Puppeteer如何从多个HTMLS生成PDF
puppeteer 生成pdf,绝对解决你的需求