当前位置: 首页 > news >正文

【nest】puppeteer 使用 addScriptTag 在页面中添加方法的方式

1.js方法支持(已测试)

1.1 先支持js

tsconfig.json

"compilerOptions": {"allowJs": true,...}

1.2 需要将抽离的方法放在js中封装

src/utils/utils.js

export function concatLabel(tagList)     {let labels = [];for (let i = 1; true; i++) {let li = tagList.querySelector(`li:nth-child(${i})`);if (li) {labels.push(li.textContent.trim());} else {break; // 当没有更多的 li 元素时,结束循环}}return labels.join('@');
}

1.3 service中使用该方法

import {concatLabel} from "./utils/utils.js";// 将该方法添加为script,因为如果不使用js的话,在页面执行的时候可能会报错
await page.addScriptTag({content: concatLabel.toString()});//使用 await page.$eval('执行页面代码const jobs = await page.$eval('.job-list-box', (el, methodName) => {return [...el.querySelectorAll('.job-card-wrapper')].map(item => {let tagList = item.querySelector('.company-tag-list');console.log("=========================")// 需要添加注解 @ts-ignore 否则编译无法通过// @ts-ignore// 由于该方法已注入页面,因此使用this即可调用let xxxxx = this.concatLabel(tagList);

2.引入 webpack 和 babel 的编译机制(未测试,记录下)

来源: https://www.xiday.com/2019/09/21/puppeteer-run-js/
有时我们可能需要在 Puppeteer 环境中执行一段 JS 代码。
根据官方提供的 API,我们有两种选择,

2.1 一种是添加 script 标签的方式引入 JS。

page.addScriptTag(options)

options <[Object]>
url <[string]> URL of a script to be added.
path <[string]> Path to the JavaScript file to be injected into frame. If path is a relative path, then it is resolved relative to [current working directory].
content <[string]> Raw JavaScript content to be injected into frame.
type <[string]> Script type. Use ‘module’ in order to load a Javascript ES6 module. See script for more details.
returns: <[Promise]<[ElementHandle]>> which resolves to the added tag when the script’s onload fires or when the script content was injected into frame.

2.2 另一种是使用page.evaluate

const result = await page.evaluate(x => {return Promise.resolve(8 * x);
}, 7);
console.log(result); // prints "56"

2.3 说明及遇到的问题

page.addScriptTag虽然可以引用本地文件作为 JS 执行,但是模块系统(ES6 Module 和 CommonJS 等)支持并不完善,部分 ES6 代码不支持 (最新 Chrome 可忽略)。
page.evaluate中的代码相当于在 DevTools 的控制台执行的,同样也有模块系统和 ES6 的问题,只是函数传值比page.addScriptTag方便。
所以我认为最好的解决方式是引入 webpack 和 babel 的编译机制。
具体方案是使用 Webpack Node API 配合 memory-fs 将要执行的 JS 文件作为入口,将编译结果输出为字符串,再通过page.evaluate执行。

2.4 具体方案

2.4.1 安装相关依赖

yarn add webpack memory-fs @babel/core @babel/preset-env babel-loader

2.4.2 编写一个buildModule函数来将指定文件作为入口,将相关模块依赖打包为 JS Bundle 字符串。

const webpack = require('webpack');
const MemoryFS = require('memory-fs');const buildModule = file => {const compiler = webpack({mode: 'development',devtool: 'cheap-module-eval-source-map',entry: require.resolve(file),output: {filename: 'bundle.js',path: '/build',},module: {rules: [{test: /\.m?js$/,exclude: /(node_modules|bower_components)/,use: {loader: 'babel-loader',},},],},});const fs = new MemoryFS();compiler.outputFileSystem = fs;return new Promise((resolve, reject) => {compiler.run(error => {if (error) {reject(error);return;}const content = fs.readFileSync('/build/bundle.js');resolve(content.toString());});});
};

2.4.3在根目录新建babel.config.js来指定打包时的 babel 配置,当然也可以复用项目已有的配置。

babel.config.js

module.exports = {presets: [['@babel/preset-env',{modules: false,useBuiltIns: 'entry',corejs: 3,targets: {chrome: 70,},},],],
};

最后,我们准备需要执行的 JS 入口文件,如果需要执行某些函数,可以将相关模块暴露到 window 对象,供 Puppeteer 使用。在这里我们将自己写的 sum 函数暴露到 window 上。
browser_run.js

import add from 'lodash/add';const sum = (...param) => [...param].reduce((a, b) => add(a, b), 0);window.sum = sum;

最终在 Puppeteer 中调用buildModule函数,传入入口文件路径,经由 webpack 打包和 babel 编译,最后通过page.evaluate函数执行。

const puppeteer = require('puppeteer');(async () => {const browser = await puppeteer.launch();const page = await browser.newPage();const scriptStr = await buildModule('./browser_run.js');await page.evaluate(scriptStr);await page.evaluate(() => {console.log('sum:', window.sum(1, 2, 3, 4, 5));});
})();

相关文章:

  • Gnu/Linux 之 C 语言函数列表初步整理
  • Python题目
  • C++基础知识——引用
  • 机器学习课程复习——逻辑回归
  • 图像编辑技术的新篇章:基于扩散模型的综述
  • ubuntu16.04升级cmake版本至3.21.0
  • vuex的配置主要内容
  • k8s集群master故障恢复笔记
  • 华为数通——OSPF
  • linux 简单使用 sftp 和 lftp命令
  • java算法:插入排序
  • 34、shell数组+正则表达式命令
  • 视频监控平台:支持交通部行业标准JT/T905协议(即:出租汽车服务管理信息系统)的源代码的函数和功能介绍及分享
  • 示例:推荐一个应用Adorner做的表单对话框
  • Linux ComfyUI安装使用;Stable Diffusion 3使用
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • JavaScript HTML DOM
  • Java程序员幽默爆笑锦集
  • Java反射-动态类加载和重新加载
  • PHP 7 修改了什么呢 -- 2
  • Promise初体验
  • Promise面试题2实现异步串行执行
  • Swoft 源码剖析 - 代码自动更新机制
  • vue-loader 源码解析系列之 selector
  • 关于Java中分层中遇到的一些问题
  • ------- 计算机网络基础
  • 类orAPI - 收藏集 - 掘金
  • 力扣(LeetCode)56
  • 扑朔迷离的属性和特性【彻底弄清】
  • 如何编写一个可升级的智能合约
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 小程序button引导用户授权
  • NLPIR智能语义技术让大数据挖掘更简单
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • $LayoutParams cannot be cast to android.widget.RelativeLayout$LayoutParams
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (Python) SOAP Web Service (HTTP POST)
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (附源码)springboot课程在线考试系统 毕业设计 655127
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (四)Android布局类型(线性布局LinearLayout)
  • (算法)Travel Information Center
  • (一)、软硬件全开源智能手表,与手机互联,标配多表盘,功能丰富(ZSWatch-Zephyr)
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • .naturalWidth 和naturalHeight属性,
  • .NET DataGridView数据绑定说明