【项目经验】详解Puppeteer入门及案例

文章目录

  • 一.项目需求及Puppeteer是什么?
  • 二.Puppeteer注意事项及常用的方法
    • 1.注意事项
    • 2.常用的方法
      • *puppeteer.launch()*
      • *browser.newPage()*
      • *page.goto()*
      • *page.on('request',()=> {})*
      • *page.evaluate()*
      • *page.$$eval()*
      • *browser.close()*
  • 三.案例代码及注释及运行结果
    • index.js(代码)
    • 运行结果

一.项目需求及Puppeteer是什么?

前端爬取别的网页并下载网页上的内容到本地。上网查到可以使用node.js的Puppeteer库来实现。下面我根据我的理解来**说一下puppeteer是什么?**参考puppeteer中文文档。
puppeteer中文意思是被操纵的木偶,从字面意思理解操纵木偶应该很简单。官方文档给的定义是:puppeteer是node的一个库(方法),Puppeteer 默认以 headless 模式运行(其实就是前端通过headless模式来运行谷歌浏览器),headless谷歌浏览器有说明,感兴趣的可以去看。也可以通过配置进行‘有头’模式去运行浏览器。

二.Puppeteer注意事项及常用的方法

1.注意事项

Puppeteer 至少需要 Node v6.4.0及以上的版本,这个在使用中需要注意。打开官方文档,发现一堆async/await,这是es7的语法。
我这个案例是下载图片到本地的案例,刚开始创建项目是需要npm init,否则项目运行不起来,还有一个注意点事在package.json里面配置type:“module”
package.json中的配置

2.常用的方法

puppeteer.launch()

创建浏览器实例,括号里面可以配置浏览器,headless:false,就是打开浏览器,反之就是关闭浏览器。

browser.newPage()

创建一个新的页面

page.goto()

加载浏览器页面,参数是要爬取网页的URL。

page.on(‘request’,()=> {})

监听浏览器请求,就是被爬取网页的请求

page.evaluate()

这个方法是为了获取被爬取网页上的内容,比如本案例的图片的url,他的参数是一个回调函数,它还可以在页面上指向一些点击事件等操作,特别注意的是这个方法传参必须是字符串

page.$$eval()

这个方法是用来获取页面元素属性和值。

browser.close()

关闭浏览器

三.案例代码及注释及运行结果

index.js(代码)

import axios from 'axios'
import path from 'path'
import fs from 'fs/promises'
import puppeteer from 'puppeteer' const __dirname = path.resolve() // 当前文件所在目录// 定义下载函数
const download = (url, dir, filename) =>new Promise(async (resolve, reject) => {try {// { responseType: 'arraybuffer' } 不加这个会乱码const { data } = await axios.get(url, { responseType: 'arraybuffer' }) // 请求图片数据fs.writeFile(path.join(dir, filename), data).then(() => resolve(filename + ' -> 下载成功')) // 写入图片} catch ({ code }) {reject({ filename, url, code }) // 抛出错误}})// 失败重试
Promise.retry = (fn, arg, count = 3) =>new Promise(async (resolve, reject) => {if (typeof fn !== 'function') reject(new Error('fn must be a function')) // 判断 fn 是否为函数if (!Array.isArray(arg)) reject(new Error('arg must be an array')) // 判断 arg 是否为数组let index = 0while (index < count) {try {resolve(await fn(...arg))return} catch (e) {index++if (index === count - 1) reject(e)}}})// 图片下载函数
const imgDownloader = async (url, dir, count = 100, cb) => {if (cb && typeof cb !== 'function') throw new Error('cb must be a function')// 浏览器加载模块async function getImgUrl(url) {if (!url) throw new Error('url must be a string')const options = {defaultViewport: {width: 1920,height: 1080},headless: true // 不打开浏览器// headless: false // 打开浏览器// slowMo: 1000 // 慢慢加载}const browser = await puppeteer.launch(options) // 创建浏览器实例const page = await browser.newPage() // 创建一个新的页面try {await page.goto(url) // 加载页面// 定义加载方法const loadAll = () =>new Promise((resolve, reject) => {// 没有数据请求后3秒 resolve() 结束异步等候var timervar timeout = () => (timer = setTimeout(() => resolve(true), 3000))// 监听网页请求page.on('request', () => {clearTimeout(timer) // 清除定时器timeout() // 重新设置定时器// 网页下拉 这部分是在浏览器操作的    !!!!!这方法不在node执行page.evaluate(cb => {const height = document.body.offsetHeightwindow.scrollTo(0, height + 500)try {const fn = eval(cb)if (typeof fn === 'function') fn()} catch (e) {console.log(e)}}, `${cb}`)})})console.log('开始加载网页数据...')await loadAll() // 加载全部页面 没有请求2秒后停止console.log('网页数据加载完毕!')// 获取图片地址const data = await page.$$eval('img', imgs => {let imgUrlList = []imgs.forEach(img => {const property = [...img.attributes] // 将img的属性转换为数组property.forEach(({ value }) => value.slice(0, 9).includes('//') && imgUrlList.push(value)) // 判断是否是url})return [...new Set(imgUrlList)].filter(i => i.indexOf('.svg', i.length - 9) == -1) // 去重})// 关闭浏览器await browser.close()const results = data.map(url => (/^http/.test(url) ? url : `https:${url}`)) // 判断是否是https协议return results // 返回结果} catch (e) {// 关闭浏览器console.log(e)await browser.close()}}// 尝试下载try {const urlList = await getImgUrl(url) // 获取图片下载地址列表await fs.access(dir).catch(() => fs.mkdir(dir, { recursive: true })) // 创建目录// 数组分片方法const slicing = (arr, count) => {if (!Array.isArray(arr)) throw new Error('arr must be an Array')if (typeof count !== 'number' || count < 1) throw new Error('count must be a number')let list = []for (let i = 0; i < arr.length; i += count) {list.push(arr.slice(i, i + count))}return list}// 下载列表const list = slicing(urlList, count)// 下载结果const results = []console.log(`${urlList.length}张照片待下载...`)const startTime = new Date() // 开始时间// 开始分片下载for (let i = 0; i < list.length; i++) {console.log(`开始下载 -> ${(i + 1) * count}`)const downloadList = list[i].map((item, index) => {// 文件名const filename = (new Array(10).join('0') + (i * count + index)).slice(-6) + '.jpg'return Promise.retry(download, [item, dir, filename])})const res = await Promise.allSettled(downloadList)results.push(...res)}// 输出下载结果results.forEach(({ value, reason }) => console.log(value || reason))// 输出结束时间const endTime = new Date() // 结束时间console.log('下载完成 -> 耗时:' + (endTime - startTime) / 1000 + 's') // 耗时} catch (e) {console.error(e)}
}// const url =
//   'https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=result&fr=&sf=1&fmq=1652366997889_R&pv=&ic=&nc=1&z=&hd=&latest=&copyright=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&dyTabStr=MCwzLDEsNCw1LDcsOCwyLDYsOQ%3D%3D&ie=utf-8&sid=&word=%E9%A3%8E%E6%99%AF'const url = 'https://www.vcg.com/sets/516942437'const dir = path.join(__dirname, 'imgs/img1') // 图片存储目录// 第一个参数 要爬取网站的地址
// 第二个参数 图片存储目录
// 第三个参数 分片下载数量
// 第四个参数 回调函数用于浏览器操作
imgDownloader(url, dir, 200, () => {const dom = document.querySelector('.jss50')dom && dom.click()
})

运行结果

运行结果
说实话感觉Puppeteer做爬虫有点屈才了,它应该有更强大的功能,大家一起慢慢学习慢慢总结吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/627176.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

USB Redirector本地安装并结合内网穿透实现远程共享和访问USB设备

文章目录 前言1. 安装下载软件1.1 内网安装使用USB Redirector1.2 下载安装cpolar内网穿透 2. 完成USB Redirector服务端和客户端映射连接3. 设置固定的公网地址 前言 USB Redirector是一款方便易用的USB设备共享服务应用程序&#xff0c;它提供了共享和访问本地或互联网上的U…

【dc-dc】世微AP5127平均电流型LED降压恒流驱动器 双色切换的LED灯驱动方案

这是一款双色切换的LED灯方案&#xff0c;12-50V 降压恒流,输出&#xff1a;6V 2.5A ​ 这是一款PWM工作模式 , 高效率、 外围简单、内置功率管&#xff0c;适用于 输入的 高 精度降压 LED 恒流驱动芯片。输出大功率可 达 25W&#xff0c;电流 2.5A。 可实现全亮/半亮功能切换…

上门按摩系统:科技与传统融合的新体验

在快节奏的现代生活中&#xff0c;人们越来越重视身心健康。传统的按摩方式虽然深受喜爱&#xff0c;却常因时间、地点的限制而无法满足需求。此时&#xff0c;上门按摩系统应运而生&#xff0c;将科技与传统的按摩技艺完美结合&#xff0c;为用户提供更便捷、个性化的服务。 上…

【Linux】自定义shell

👑作者主页:@安 度 因 🏠学习社区:安度因 📖专栏链接:Linux 文章目录 获取命令行前置字段命令行输入解析命令行普通指令的执行子进程执行命令指令类型判断 && 内建命令总结 &&a

uniapp的nvue是什么

什么是nvue uni-app App 端内置了一个基于 weex 改进的原生渲染引擎&#xff0c;提供了原生渲染能力。 在 App 端&#xff0c;如果使用 vue 页面&#xff0c;则使用 webview 渲染&#xff1b;如果使用 nvue 页面(native vue 的缩写)&#xff0c;则使用原生渲染。一个 App 中可…

深入解析JavaScript中new Function语法

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ ​ ✨ 前言 Function是JavaScript中非常重要的内置构造函数,可以用来动态创建函数…

十大必备功能:打造高效知识库的关键因素

一个好的产品知识库应该成为客户了解产品功能、解决故障和满足产品相关查询的重要资源。但如果没有合理地维护和更新&#xff0c;其可能就失去了存在的价值。 知识库的有效性取决于其包含的信息是否全面、准确和实用。而要实现这一点&#xff0c;需要关注一些关键功能。 以人…

go中如何进行单元测试案例

一. 基础介绍 1. 创建测试文件 测试文件通常与要测试的代码文件位于同一个包中。测试文件的名称应该以 _test.go 结尾。例如&#xff0c;如果你要测试的文件是 math.go&#xff0c;那么测试文件可以命名为 math_test.go。 2. 编写测试函数 测试函数必须导入 testing 包。每…

2024年甘肃省职业院校技能大赛信息安全管理与评估 样题一 理论题

竞赛需要完成三个阶段的任务&#xff0c;分别完成三个模块&#xff0c;总分共计 1000分。三个模块内容和分值分别是&#xff1a; 1.第一阶段&#xff1a;模块一 网络平台搭建与设备安全防护&#xff08;180 分钟&#xff0c;300 分&#xff09;。 2.第二阶段&#xff1a;模块二…

微信小程序怎么引入webview的url是本地的路径

当微信小程序访问类似http://10.27.0.15:8065/#/my这样的地址的时候会出问题。但是我们也不能每次把写的H5的代码发布在看效果啊&#xff1f; 只需要修改一个地方就可以啦。

Transformer 位置编码

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

LLM(十)| Tiny-Vicuna-1B:Tiny Models轻量化系列Top One

在过去的一年里&#xff0c;见证了LLM的蓬勃发展&#xff0c;而模型的参数量也不断刷新记录&#xff0c;在2023年下半年&#xff0c;外界传言GPT-4是一个专家混合模型。因此&#xff0c;如果你想用人工智能做点什么&#xff0c;你需要IBM或NASA类似的计算能力&#xff1a;你怎么…

纯c++简易的迷宫小游戏

一个用c写的黑框框迷宫 适合新手入门学习 也适合大学生小作业 下面附上代码 总体思路 初始化游戏界面&#xff1a;设置迷宫的大小&#xff08;WIDTH和HEIGH&#xff09;&#xff0c;生成迷宫地图&#xff08;map&#xff09;&#xff0c;包括墙壁、空地、起点和终点。显示…

【K12】Python写串联电阻问题的求解思路解析

问题源代码 方法&#xff1a;calculate_circuit_parameter 构造题目&#xff1a; 模板&#xff1a; 已知电阻R1为 10Ω&#xff0c;电阻R2为 5Ω&#xff0c;电压表示数为2.5V&#xff0c;求电源电压U&#xff1f; 给合上面题目&#xff0c;利用Python程序&#xff0c;可以任…

【论文笔记合集】卷积神经网络之深度可分离卷积(Depthwise Separable Convolution)

本文作者&#xff1a; slience_me 我看的论文地址&#xff1a;MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications 内容 1. 标准卷积 假设输入为DFDFM&#xff0c;输出为输入为DFDFN&#xff0c;卷积核为DKDKM&#xff0c;共有N个卷积核进…

LeetCode刷题---随机链表的复制

解题思路&#xff1a; 使用哈希表来解决该问题 因为题中要求是深拷贝 首先对原链表遍历&#xff0c;将原链表每个节点和新链表每个节点形成对应关系&#xff0c;存入到哈希表中&#xff0c;key为原链表的节点&#xff0c;value为新链表的节点。 之后重置辅助链表指向原链表头节…

墨刀原型-实现轮播图功能

在墨刀中实现轮播图效果&#xff0c;可以按照以下步骤进行操作&#xff1a; 1.添加轮播图组件&#xff1a;在墨刀的组件面板中&#xff0c;找到轮播图组件并将其拖拽到画布上。 2.上传轮播图&#xff1a;在右侧的属性面板中&#xff0c;你可以上传你的轮播图图片。点击“”按钮…

动态pv(nfs方式挂载)

1、定义 发布pvc之后可以生成pv&#xff0c;还可以在共享服务器上直接生成挂载目录 pvc直接绑定和使用pv 2、动态pv依赖两个组件 &#xff08;1&#xff09;provisioner卷插件&#xff1a;k8s本身支持的动态pv创建不包括nfs&#xff0c;需要声明和安装一个外部插件provisio…

NET Core发布 HTTP Error 500.31 - Failed to load ASP.NET Core runtime

记录一下踩过的坑&#xff1a; 首先&#xff0c;不论是500.31还是500.30 &#xff0c;首先确保安装了三个文件 1.NET Core RunTime 2.NET SDK 3.NET Hosting 其次&#xff0c;确保三个文件的版本一致&#xff0c;如下&#xff1a; 要装就统一装同一个大版本&#xff0c;不要东…

Linux第28步_编译“修改正点原子TF-A源码中的Makefile并编译生成新的TF-A 固件”

了解学习内容&#xff1a; 1)、正点原子STM32MP157开发板使用的主控型号是STM32MP157DAA1&#xff1b; 2)、“linux /atk-mp1/atk-mp1/alientek_tf-a/tf-a-stm32mp-2.2.r1”目录下的文件是正点原子STM32MP157D开发板的“TF-A源码”。 3)、“linux /atk-mp1/atk-mp1/alientek…