dom截图——探究长截图的极限

news2024/9/23 11:20:54

长截图问题

问题:使用dom-to-image和html2canvas来进行长截图会出现一个问题,如果图片非常长,一些图片会只加载一半,如果图片再长一些,截图就会为空。

目前我测试的结果:截图的大小在8mb出现图片缺了的情况
截图为空不是库的bug,是浏览器到了极限,无法输出了,这时cpu飙到快100%。

图片缺失这个情况还可以优化,可以想办法把dom进行拆分,再分别截图,最后拼接图片

优化长截图的思路:

  1. 还是用js里的dom-to-image,不断拆分dom,分别截图,最后合并
  2. 使用selenium截图,滚动截图,再用opencv拼接图片

上面两个思路,是不是能实现无限长的截图呢?

方法一:按理说应该能无限长的,但是浏览器扛不住,我的测试结果是截图大小在18mb左右,再大一些就不输出了。
方法二:这个方法能实现无限长的截图,但太慢了

代码实现

方法一:推荐做法

核心思想:把如下图所示拆分的dom分别使用dom-to-image截图,再通过canvas合并图片,最后下载

要截图的完整dom
在这里插入图片描述
拆分的dom1
在这里插入图片描述
拆分的dom2
在这里插入图片描述
拆分的dom3
在这里插入图片描述

const start = async() => {
	// 拆分dom
	const domArr = this.splitChatDom()
	// 生成图片到imageList
	domArr.forEach((domList,index) => {
	  const dom = this.mergeChat(domList)
	  document.body.appendChild(dom)
	  this.createImage(dom,index)
	})
	await this.sleep(5000);
	// 合并图片
	this.mergeImage()
	await this.sleep(5000);
	// 下载
	this.download(this.canvas.toDataURL('image/png'),this.getFilename())
}
start()

把需要截图的子元素取出来,放到数组中

// 拆分chat子元素
splitChatDom() {
  let chat = document.querySelector("#chat")
  let chat_inner = chat.getElementsByClassName("chat-inner")[0]
  const dom_length = chat_inner.children.length
  let childs = chat_inner.children

  let domArr = []
  let i = 0;
  // 拆分个数
  const num = 20
  while (i < dom_length) {
    let arr = []
    for (let j=0;  j< num && i+j<dom_length; j++) {
      const element = childs[i+j];
      arr.push(element)
    }
    domArr.push(arr)
    i+=num
  }
  return domArr
},

还原dom的父节点

mergeChat(domList) {
  let chat = document.createElement("div")
  chat.id = "chat"
  let chat_inner = document.createElement("div")
  chat_inner.className = "chat-inner"
  chat.appendChild(chat_inner)
  // console.log(domList);
  domList.forEach((dom) => {
    chat_inner.appendChild(dom)
  })
  
  return chat
},

分别截图

// 根据dom创建图片
createImage(dom,index) {
  domtoimage
    .toPng(dom, { quality: 1 })
    .then((dataUrl) => {
      this.imageList[index] = dataUrl
      this.heightList[index] = dom.offsetHeight
    })
},

合并截图

// 合并图片
mergeImage() {
  this.canvas = null
  let canvas = document.createElement("canvas")
  let context = canvas.getContext("2d")
  context.scale(0.8,0.8)
  const scale = 0.8;
  const width = 1000 * scale; // 确保canvas的尺寸和图片一样
  const canvas_height = this.heightList.reduce((a,b)=>a+b-170) * scale;
  canvas.width = width
  canvas.height = canvas_height
  let sumHeight = 0
  for (let i = 0; i < this.imageList.length; i++) {
    const dataUrl = this.imageList[i];
    const height = this.heightList[i]
    let img = new Image()
    img.src = dataUrl
    img.width = width
    img.height = height
    img.onload = () => {
      context.drawImage(img,0,sumHeight,width,height * scale)
      sumHeight += (height - 170) * scale
      this.mergeFlag[i] = true
    }
  }
  this.canvas = canvas
},

暂停方法,需要在异步方法中调用

sleep(time){
  return new Promise((resolve)=>{
    setTimeout(()=>{
      resolve();
    }, time)
  })
},

方法二:效率低

def save_screenshot(url, fp_pic):
    chrome_options = webdriver.ChromeOptions(); 
    chrome_options.add_experimental_option("excludeSwitches", ['enable-automation']);
    driver = webdriver.Chrome(options=chrome_options);

    driver.get(url)
    time.sleep(5)
    
    # 获取要截图的dom的高度
    chat = driver.find_element_by_id("chat")
    dom_height = chat.get_attribute("offsetHeight")
    offset_top = chat.get_attribute('offsetTop')
    page_height = 500

    driver.set_window_size(1080, page_height)
    # 滚动到要截图的位置
    #driver.execute_script("arguments[0].scrollIntoView();", chat)
    driver.execute_script(
      'window.scrollBy(0,{});'.format(int(offset_top))
    )
    driver.save_screenshot('temp.png')

    n = int(dom_height) // (page_height-85) # 需要滚动的次数
    base_mat = np.atleast_2d(Image.open('temp.png')) # 打开截图并转为二维矩阵
    for i in range(n):
        print(i,page_height*(i+1))
        driver.execute_script(
          'window.scrollBy(0,{});'.format(page_height-85)
          #'window.scrollTo(0,{});'.format(page_height*(i+1)+int(offset_top))
        )
        driver.save_screenshot(f'temp_{i}.png') # 保存截图
        mat = np.atleast_2d(Image.open(f'temp_{i}.png')) # 打开截图并转为二维矩阵
        base_mat = np.append(base_mat, mat, axis=0) # 拼接图片的二维矩阵
        Image.fromarray(base_mat).save(fp_pic, format='PNG')
        os.remove(f'temp_{i}.png')

    os.remove('temp.png')
    driver.quit()
    
if __name__ == "__main__":
	url = ''
	save_screenshot(url,'aaa.png')
	# 缩放图片
	img = cv2.imread('aaa.png')
	img = img[:,:1250]
	img = cv2.resize(img,None,fx=0.64, fy=0.64, interpolation = cv2.INTER_CUBIC)
	cv2.imwrite('aaa.png',img)

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

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

相关文章

Blender 编辑骨骼动画,重复动作,并导出动画为视频

文章目录制作动作动画.重复动作.导出动画为视频制作动作动画. 1 进入姿态模式。调整各个部位的位置。调整好后&#xff0c;A&#xff0c;全选&#xff0c;I 记录置和旋转并创建一个关键帧 2 如果回放时间轴上没有关键帧&#xff0c;可以去 动画时间表/动作编辑器 窗口查看。注…

CVE-2019-2725漏洞复现

前言 在学习内网过程中遇到了weblogic比较常见的漏洞&#xff0c;编号是cve-2019-2725,之前没有总结过&#xff0c;于是本篇文章给大家总结归纳一下该漏洞的利用方法与原理。 基础知识 cve-2019-2725漏洞的核心利用点是weblogic的xmldecoder反序列化漏洞&#xff0c;攻击步骤…

SpringBoot学习超详细 第三 实用开发篇

目录 一、启动热部署 二、配置高级 三、常用 计量单位 四、开启数据校验 五、测试 第一种web环境测试 第二种web环境测试 ​编辑 第三种web环境测试 第四种web环境测试 第五种web环境测试 六、数据层解决方案 1、SQL 七、NoSQL&#xff08;redis&#xff09; 1、Red…

ArcMap制图相关问题

ArcMap是一款我们常用的GIS桌面端应用&#xff0c;在完成一系列空间分析任务后&#xff0c;我们通常会选择用专题图来呈现结果。今天&#xff0c;我们就来聊一聊有关ArcMap成图的一些问题。注&#xff1a;此处演示使用版本为ArcGIS10.4.1如何实现“一幅多图”在绘制研究区区位图…

AC7811-PWDT脉冲宽度检测

PWDT是Pulse Width Detect Timer&#xff08;脉冲宽度检测定时器&#xff09;缩写。可作为测量脉冲宽度的工具或作为16位定时器。 功能 检测脉冲宽度 可编程起始测量触发沿 支持 3 个霍尔传感器的信号输入测量 支持来自模拟比较器的 3 个输入 定时器 在禁用定时器或在正…

【JavaScript】DOM 操作元素样式和元素类名

文章目录【JavaScript】DOM 操作元素样式和元素类名一. 操作元素样式(1)操作行内元素1. 获取元素的样式2. 设置元素的样式(2) 操作非行内元素和行内元素1. 获取元素的样式二. 操作元素类名(1) className1. 获取元素的类名2. 设置元素的类名3. 追加元素的类名(2) classList1. 获…

【谷粒商城基础篇】项目简介与分布式概念

谷粒商城笔记合集 分布式基础篇分布式高级篇高可用集群篇简介&环境搭建项目简介与分布式概念&#xff08;第一、二章&#xff09;基础环境搭建&#xff08;第三章&#xff09;整合SpringCloud整合SpringCloud、SpringCloud alibaba&#xff08;第四、五章&#xff09;前端知…

如何使用WebRTC建立一个视频会议App(二)

如何使用WebRTC建立一个视频会议App(一)_音视频开发老马的博客-CSDN博客 服务器接收到‘’准备好了‘’的信息并通过发送同样信息通知第一个参与者。这就开始了被称作发信的交换信息过程&#xff0c;因此我们需要添加相应的handlers到client.js文件中。 当第一个参与者接收到准…

BGP在数据中心的应用3——构建自动化的BGP配置

注&#xff1a; 本文根据《BGP in the Datacenter》整理&#xff0c;有兴趣和英文阅读能力的朋友可以直接看原文&#xff1a;https://www.oreilly.com/library/view/bgp-in-the/9781491983416/上一部分笔记请参考&#xff1a;https://blog.csdn.net/tushanpeipei/article/deta…

【回答问题】ChatGPT上线了!推荐20个姿态估计深度学习模型

推荐20个姿态估计深度学习模型 好的&#xff0c;这里是 20 个深度学习模型用于姿态估计的推荐&#xff1a; 2D/3D Convolutional Neural Networks Recurrent Neural Networks Self-supervised Learning Generative Adversarial Networks Attention-based Networks Graph Neur…

git add .卡住不动

问题描述 代码修改完了准备上传到github&#xff0c;使用 git add . 命令发现一直卡住不动了。没办法只能强行退出&#xff0c;如下图所示。 解决办法 研究了一番&#xff0c;发现了这类问题的套路&#xff0c;在此给大家提供一个debug的思路。 首先需要知道&#xff0c;git a…

保姆级手把手图文并茂教你配置MAC系统Flutter环境

Flutter 是什么 Flutter是Google开源的构建用户界面&#xff08;UI&#xff09;工具包&#xff0c;帮助开发者通过一套代码库高效构建多平台精美应用&#xff0c;支持移动、Web、桌面和嵌入式平台。Flutter 开源、免费&#xff0c;拥有宽松的开源协议&#xff0c;适合商业项目…

返回一个数组中所有元素的小数部分、整数部分为两个数组 numpy.modf()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 返回一个数组中所有元素的 小数部分、整数部分 为两个数组 numpy.modf() [太阳]选择题 关于以下python代码表述错误的一项是? import numpy as np anp.array([-0.5,1,1.5]) print…

《对线面试官》| 高频计算机网络面试题

目录1、说说 OSI 七层模型和 TCP/IP 四层模型的关系和区别2、说说 TCP 与 UDP 的区别3、TCP 是如何实现数据的可靠性&#xff1f;4、 TCP 协议如何提高传输效率&#xff1f;5、你知道 TCP 如何处理拥塞吗&#xff1f;6、为什么 TCP 链接需要三次握手&#xff0c;两次不可以么&a…

小程序后台数据交互-个人中心

目录 一&#xff0c;获取用户昵称和头像 登录过程 二&#xff0c;登录-小程序 三&#xff0c;后台 小程序服器配置 一&#xff0c;获取用户昵称和头像 登录过程 小程序登录 小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识&#xff0c;快速建立小…

SOFARegistry | 聊一聊服务发现的数据一致性

文&#xff5c;肖健&#xff08;花名&#xff1a;昱恒&#xff09;蚂蚁集团技术专家专注于服务发现领域&#xff0c;目前主要从事蚂蚁注册中心 SOFARegistry 设计、研发工作。本文 9492 字 阅读 24 分钟PART. 1前言1.1 什么是服务发现在微服务的体系中&#xff0c;多个应用程序…

Hydra(九头蛇)工具使用

Hydra工具使用1.Hydra简介1.1.Hydra介绍1.2.Hydra支持协议1.3.Hydra下载2.Hydra使用2.1.Hydra基本使用方式2.2.Hydra常用参数2.3.Hydra注意事项2.4.Kail字典2.5.Hydra密码生成器3.Hydra案例3.1.创建账号密码文件3.2.SSH协议爆破3.2.1.靶机信息3.2.2.开始爆破3.2.2.1.不知账号3.…

C#语言实例源码系列-游戏-实现华容道

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断反思前进的过程。在这个过程中…

Rust个人学习之聊聊Rust的变量

今天看了Rust的变量相关内容&#xff0c;Rust变量很重要的一个设计点是: Rust变量默认是不可变的。 很难理解? 我们接下来一起研究一下Rust的变量&#xff0c; 在Rust中变量是通过关键字let进行声明的 let x 5; 上述语句就是定义了一个名称为x的变量&#xff0c;变量的值为…

修改esp idf hello_world_main.c,验证Strategy 策略基类。出现一个问题,加以解决

修改esp32 idf hello_world_main.c&#xff0c;验证Strategy 策略基类。出现几个问题&#xff0c;加以解决&#xff1a; 错误&#xff1a; …/main/hello_world_main.cpp: In function ‘void test01()’: …/main/hello_world_main.cpp:77:12: error: deleting object of ab…