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

Flask集成Selenium实现网页截图

先看效果

程序实现的功能为:截取目标网址对应的页面,并将截取后的页面图片返回到用户端,用户可自由保存该截图。

支持的url参数如下:

url目标网址(必填项),字符串类型,如:https://www.baidu.com

w:截图宽度(选填项),默认为浏览器的可视窗口宽度,数值类型

h:截图高度(选填项),默认为浏览器的可视窗口高度(包含滚动区域),数值类型

p:截取的屏幕数量,默认为全部(选填项),数值类型

offset:最后一屏的偏差高度,默认为 0(选填项),数值类型

headless:是否打开浏览器,默认为 静默运行(选填项),两者的区别如下:

显式运行:截图更精确,但占用的系统资源相对多一点。

静默运行:在截图精度上有一点丢失(肉眼几乎看不出),但占用更少的系统资源。

最简请求示例:

以静默状态截取百度首页

http://127.0.0.1:8181/?url=https://www.baidu.com

完整请求示例:

以显式状态按指定的宽高截取百度首页

http://127.0.0.1:8181/?url=https://www.baidu.com&w=800&h=500&p=1&offset=0&headless=0

实现代码

import io
import os
import time
import logging
from uuid import uuid4
from PIL import Image
from screeninfo import get_monitors
from flask import Flask, request, send_file
from selenium import webdriver
from selenium.webdriver.chrome.service import Service# 网页截图存储目录
image_folder = "static"app = Flask(__name__)# 禁止控制台输出请求信息
logging.getLogger('werkzeug').disabled = True
# 设置总的日志输出级别
app.logger.setLevel(logging.ERROR)# 404错误处理
@app.errorhandler(404)
def page_not_found(e):# 直接返回字符串return "您请求的资源不存在!", 404# 500错误处理
@app.errorhandler(500)
def internal_error(e):return "服务器内部错误!", 500# 捕获所有未处理异常
@app.errorhandler(Exception)
def handle_exception(e):return "发生错误,请稍后再试!", 500@app.route('/')
def process_request():url = request.args.get('url')if not url:return '无效的请求参数!'args = request.args# 截取的屏数,默认为全部pages = int(args.get('p', 0))# 最后一屏的偏差高度offset_height = int(args.get('offset', 0))# 默认使用无头模式,headless=0 表示使用有头模式headless = args.get('headless')# 捕获网页截图img_file = init(url, pages, offset_height, headless)if img_file is None:return '网页截取失败!'# 调整图片尺寸img = Image.open(img_file)(width, height) = get_image_size(img, args.get('w'), args.get('h'))# 高质量抗锯齿img = img.resize((width, height), Image.Resampling.LANCZOS)# 转换为字节流返回img_io = io.BytesIO()img.save(img_io, 'PNG')img_io.seek(0)return send_file(img_io, mimetype='image/png')def init(url, pages=0, offset_height=0, headless=None):chrome_options = webdriver.ChromeOptions()# 截图时建议关闭无头模式if headless is None:chrome_options.add_argument('--headless=new')chrome_options.add_argument('log-level=3')chrome_options.add_argument('--disable-gpu')chrome_options.add_argument('--disable-infobars')chrome_options.add_argument('--ignore-certificate-errors')chrome_options.add_experimental_option('excludeSwitches', ['enable-logging', 'enable-automation'])service = Service()service.path = 'ChromeV123/chromedriver.exe'chrome_options.binary_location = 'ChromeV123/chrome-win64/chrome.exe'driver = webdriver.Chrome(options=chrome_options, service=service)if headless is None:# 无头模式需要设置窗口尺寸set_window_size(driver)else:driver.maximize_window()try:driver.get(url)time.sleep(2)file_name = capture_full_page(driver, pages, offset_height)finally:driver.quit()return file_name# 设置浏览器窗口宽高
def set_window_size(driver):primary_monitor = get_monitors()[0]width = primary_monitor.width + 16height = primary_monitor.height - 31driver.set_window_size(width, height)'''
截取整个网页,包含滚动部分,保存路径为当前项目的{image_folder}/screenshot目录下
pages 截取的屏数,默认为全部
offset_height 偏差纠正,处理最后一屏的特殊参数,需传入合适的数值(注意:无头模式和界面模式该参数值可能是不同的)
'''
def capture_full_page(driver, pages=0, offset_height=0):file_name = Nonetry:date = time.strftime("%Y%m%d", time.localtime(time.time()))root_path = f'{image_folder}/screenshot/{date}'if not os.path.exists(root_path):os.makedirs(root_path)file_name = '{}/{}.png'.format(root_path, uuid4())# 获取网页可视区域宽高inner_width = driver.execute_script("return window.innerWidth")inner_height = driver.execute_script("return window.innerHeight")# 获取网页高度height = get_page_height(driver, pages)# 滚动截图,创建一个指定宽高的白色背景,用于在上面拼接图片screenshot = Image.new('RGB', (inner_width, height if pages == 0 else min(inner_height*pages, height)), (255, 255, 255))scroll_total = 0num = 0# 每次滚动一屏,一屏的高度为 inner_heightfor scroll_height in range(0, height, inner_height):if pages > 0 and num >= pages:breakdriver.execute_script("window.scrollTo(0, %d)" % scroll_height)time.sleep(0.5)screenshot_part = driver.get_screenshot_as_png()img = Image.open(io.BytesIO(screenshot_part))screenshot.paste(img, (0, scroll_height, img.width, scroll_height + img.height))scroll_total = scroll_heightnum += 1# 最后一屏需要特殊处理if scroll_total > 0 and scroll_total < height and height-scroll_total < inner_height:driver.execute_script(f"window.scrollTo(0, {height})")time.sleep(0.5)# 截取剩余区域img_file = capture_region(driver, 0, inner_height - (height - scroll_total) - offset_height, inner_width, inner_height)if img_file is not None:img = Image.open(img_file)screenshot.paste(img, (0, scroll_total, img.width, scroll_total + img.height))os.remove(img_file)# 保存截图screenshot.save(file_name)except:passreturn file_name'''
截取指定区域,保存路径为当前项目的screenshot/region目录下
left_top_x 左上角X轴坐标
left_top_y 左上角Y轴坐标
right_bottom_x 右下角X轴坐标
right_bottom_y 右下角Y轴坐标
'''
def capture_region(driver, left_top_x, left_top_y, right_bottom_x, right_bottom_y):try:date = time.strftime("%Y%m%d", time.localtime(time.time()))root_path = f'{image_folder}/screenshot/{date}'if not os.path.exists(root_path):os.makedirs(root_path)file_name = '{}/{}.png'.format(root_path, uuid4())screenshot = driver.get_screenshot_as_png()img = Image.open(io.BytesIO(screenshot))region = img.crop((left_top_x, left_top_y, right_bottom_x, right_bottom_y))region.save(file_name)return file_nameexcept Exception:passreturn None# 计算图片宽高
def get_image_size(img, target_width=None, target_height=None):original_width, original_height = img.size# 默认为图片原始宽高if not target_width and not target_height:return (original_width, original_height)# 计算目标尺寸if target_width and target_height:# 情况1:指定宽高参数crop_width = min(int(target_width), original_width)crop_height = min(int(target_height), original_height)elif target_width:# 情况2:仅指定宽度,高度按比例计算crop_width = min(int(target_width), original_width)crop_height = int(original_height * (crop_width / original_width))else:# 情况3:仅指定高度,宽度按比例计算crop_height = min(int(target_height), original_height)crop_width = int(original_width * (crop_height / original_height))return (crop_width, crop_height)# 获取页面实际高度(包含滚动区域)
def get_page_height(driver, pages):last_scroll_height = 0# 获取浏览器可视窗口高度inner_height = driver.execute_script("return window.innerHeight")total_height = inner_heightnum = 0while True:if pages > 0 and num >= pages+1:breakscroll_height = driver.execute_script("return window.scrollY") + inner_heightif scroll_height == last_scroll_height:breaklast_scroll_height = scroll_height# 之所以每次滚动一屏,是因为某些网站的图片资源,只有在滚动到当前可视窗口时,才会加载出来driver.execute_script(f"window.scrollTo(0, {total_height})")total_height += inner_heightnum += 1time.sleep(2)return last_scroll_heightif __name__ == '__main__':app.run(host='0.0.0.0', port=8181)

 🏷️ 如有疑问,可以关注 我的知识库,直接提问即可。

相关文章:

Flask集成Selenium实现网页截图

先看效果 程序实现的功能为&#xff1a;截取目标网址对应的页面&#xff0c;并将截取后的页面图片返回到用户端&#xff0c;用户可自由保存该截图。 支持的url参数如下&#xff1a; url&#xff1a;目标网址&#xff08;必填项&#xff09;&#xff0c;字符串类型&#xff0c…...

机顶盒CM311-5s纯手机免拆刷机,全网通,当贝桌面

需要用到的工具 安卓手机一台 甲壳虫adb助手&#xff08;安卓app&#xff09; OTG转换线一个&#xff08;或者用usb&#xff0c;typec双头的U盘一个&#xff0c;未测试&#xff09; 8g U盘一个 用到的刷机文件 1.放入手机中的文件 misc recovery 2. 放入U盘根目录 upda…...

知识图谱:AI时代语义认知的底层重构逻辑

在生成式人工智能&#xff08;GEO&#xff09;的技术架构中&#xff0c;知识图谱已从辅助性工具演变为驱动机器认知的核心神经中枢。它通过结构化语义网络的重构&#xff0c;正在突破传统数据处理的线性逻辑&#xff0c;建立机器对复杂业务场景的深度理解能力。 一、语义解构&a…...

centos7安装MySQL(保姆级教学)

在 Linux 系统的软件管理中&#xff0c;YUM&#xff08;Yellowdog Updater, Modified&#xff09;包管理器是不可或缺的工具&#xff0c;而 YUM 源的选择与配置直接影响着软件安装与更新的效率。本文将深入解析网络 YUM 源的分类&#xff0c;详细介绍如何使用知名平台提供的 YU…...

2025.5.23 【ZR NOI模拟赛 T3】高速公路 题解(容斥,高维前缀和,性质)

非常牛的题&#xff0c;记录一下思路。 传送门 题意 有一张 n n n 个点的无向图&#xff0c;每个点有一个颜色 c i c_i ci​&#xff0c;满足 c i ∈ [ 1 , k ] c_i \in [1, k] ci​∈[1,k]。 图是由 m m m 条链组成&#xff0c;满足任意一个点恰好只在一条链上。对于一…...

QGIS新手教程2:线图层与多边形图层基础操作指南(点线互转、中心点提取与WKT导出)

QGIS新手教程&#xff1a;线图层与多边形图层基础操作指南&#xff08;点线互转、中心点提取与WKT导出&#xff09; 目录 QGIS新手教程&#xff1a;线图层与多边形图层基础操作指南&#xff08;点线互转、中心点提取与WKT导出&#xff09;&#x1f4cc; 引言第一部分&#xff1…...

nova14 ultra,是如何防住80°C热水和10000KPa水压冲击的?

暴雨突袭&#xff0c;手忙脚乱护住背包&#xff0c;却担心手机被雨水浸湿&#xff1b;泳池里想记录美好时刻&#xff0c;却担心手机掉入水中 &#xff1b;厨房里充满了高温水汽&#xff0c;近距离拍摄美食瞬间&#xff0c;手机屏幕花屏&#xff0c;让人失去了对美食的兴趣…… …...

Spring Boot项目中实现单点登录(SSO)完整指南

单点登录(Single Sign-On, SSO)是一种身份验证机制&#xff0c;允许用户使用一组凭证(如用户名和密码)登录多个相关但独立的系统。 一、单点登录的核心原理 SSO的核心原理使集中认证、分散授权&#xff0c;主要流程如下&#xff1a; 1.用户访问应用A 2.应用A检查本地会话&a…...

Windows环境下Redis的安装使用与报错解决

最近在做项目的时候需要用到Redis&#xff0c;本来没觉得是什么麻烦&#xff0c;下载安装使用一步到位的事&#xff0c;但紧随而来的问题&#xff0c;让我开始怀疑人生&#xff0c;再加上代码跑不出来&#xff0c;我还专门找人给我看看怎么个是&#xff0c;结果就是单纯的Redis…...

鸿蒙完整项目-仿盒马App(一)首页静态页面

跟着鸿蒙小林博主&#xff0c;练习下项目~记录下首页的搭建,后续继续完善和整体项目完成会进行布局修改&#xff0c;先按照博主的跟做&#xff0c;后续在改 1.分为底部整体框架搭建 2.首页布局&#xff08;顶部搜索、新人专享、金刚区&#xff08;两个不同集合数据&#xff09…...

大模型(4)——Agent(基于大型语言模型的智能代理)

大模型Agent是一种基于大型语言模型&#xff08;LLM&#xff09;的智能系统&#xff0c;能够自主感知环境、规划任务、调用工具并完成复杂目标。其核心原理是将大模型的推理能力与外部行动能力结合&#xff0c;实现从“思考”到“行动”的闭环。以下是其原理详解与实现方法&…...

39-居住证管理系统(小程序)

技术栈: springBootVueMysqlUni-app 功能点: 群众端 警方端 管理员端 群众端: 1.首页: 轮播图展示、公告信息列表 2.公告栏: 公告查看及评论 3.我的: 联系我们: 可在线咨询管理员问题 实时回复 居住证登记申请 回执单查看 领证信息查看 4.个人中心: 个人信息查看及修改…...

WPF【11_4】WPF实战-重构与美化(MVVM 架构)

11-9 【理论】MVVM 架构 在 WPF 项目中&#xff0c;我们主要采用的是一种类似 MVC 的架构&#xff0c;叫做 MVVM。 MVVM 继承了 MVC 的理念&#xff0c;是 Model-View-ViewModel 的缩写&#xff0c;中文意思是模型、视图、视图模型。这三个词分开看我们都能看懂&#xff0c;不…...

计算逆时针夹角(有向角度)——CAD c# 实现两条线(向量)的逆时针夹角

效果如下&#xff1a; 附部分代码如下&#xff1a; public void 逆时针夹角Demo(){// 获取当前 CAD 文档和编辑器Document doc Application.DocumentManager.MdiActiveDocument;Editor ed doc.Editor;Database db doc.Database;try{Point3d vec1Start, vec1End;if (!GetTwoP…...

鸿蒙OSUniApp 开发带有通知提示的功能组件#三方框架 #Uniapp

使用 UniApp 开发带有通知提示的功能组件 在移动应用开发中&#xff0c;通知提示&#xff08;Notification/Toast/Alert&#xff09;是提升用户体验和交互效率的重要手段。无论是表单校验、操作反馈、系统消息还是营销推送&#xff0c;合理的通知提示都能帮助用户及时获取关键…...

前端EXCEL插件智表ZCELL数据源功能详解

一、数据源功能介绍 前端EXCEL插件智表ZCELL提供了强大的数据源管理功能&#xff0c;使开发者能够灵活地在电子表格中集成和管理结构化数据。数据源功能主要分为两种类型&#xff1a; 卡片式数据源&#xff1a;适合展示和编辑单个数据记录 表格式数据源&#xff1a;适合处理表…...

打卡第31天:模块和库的导入

重复内容 知识点回顾&#xff1a; 1.导入官方库的三种手段 2.导入自定义库/模块的方式 3.导入库/模块的核心逻辑&#xff1a;找到根目录&#xff08;python解释器的目录和终端的目录不一致&#xff09; 作业&#xff1a;自己新建几个不同路径文件尝试下如何导入 导入机制的核…...

LLM Coding

AI Coding 深度解析&#xff1a;探索编程新范式与未来趋势 | w3cschool笔记https://www.phodal.com/blog/ai-friendly-architecture/bmadcode/BMAD-METHOD: Breakthrough Method for Agile Ai Driven Development...

Qt 的多线程

Qt 中的多线程主要用于处理耗时操作,避免阻塞主线程(UI 线程),从而提高程序的响应性和运行效率。以下是 Qt 多线程的相关技术总结: 常见的多线程实现方式 继承 QThread 类 :最基础的实现方式,具体步骤为继承 QThread 类,重写其 run() 函数,在 run() 函数中编写线程要…...

【请关注】各类MySQL数据备份还原分享

MySQL数据备份的全部方法 MySQL数据备份是数据库管理中的关键任务,以下是MySQL数据备份的全面方法总结: 一、逻辑备份方法 1. **mysqldump工具** - 最常用的备份工具 - 命令示例: >bash mysqldump -u [username] -p[password] [database_name] > backup.sql > -…...

Go语言方法与接收者 -《Go语言实战指南》

在 Go 中&#xff0c;方法是绑定到某个类型上的函数。与普通函数不同&#xff0c;方法具有一个“接收者&#xff08;receiver&#xff09;”&#xff0c;用于指定它是哪个类型的“方法”。 一、方法的定义语法 func (接收者名 接收者类型) 方法名(参数列表) 返回值列表 {// 方…...

基于 STM32 的农村污水处理控制系统设计与实现

摘要 针对农村污水处理自动化程度低、运维成本高的问题,本文设计了一种基于 STM32 单片机的污水处理控制系统。系统通过多传感器实时监测水质参数,结合 PID 控制算法实现污水处理全流程自动化,并集成远程监控功能,满足农村地区低成本、易维护的需求。 一、硬件系统设计 …...

【Linux】进程 信号的产生

&#x1f33b;个人主页&#xff1a;路飞雪吖~ &#x1f320;专栏&#xff1a;Linux 目录 一、掌握Linux信号的基本概念 &#x1f320;前台进程 VS 后台进程 &#x1f320; 小贴士&#xff1a; &#x1fa84;⼀个系统函数 --- signal() &#x1fa84;查看信号 --- man 7 sign…...

实时操作系统革命:实时Linux驱动的智能时代底层重构

一、智能时代对实时性的终极挑战 在万物互联的智能时代&#xff0c;人类对机器响应速度的期待已突破物理极限。当工业机器人以亚毫米级精度执行微米级加工任务&#xff0c;当自动驾驶系统在130公里时速下需在10毫秒内完成决策切换&#xff0c;当医疗机器人需在5毫秒内响应神经…...

NGINX HTTP/3 实验指南安装、配置与调优

一、HTTP/3 简介 基于 QUIC&#xff1a;在 UDP 之上实现的多路复用传输&#xff0c;内置拥塞控制与前向纠错&#xff0c;无需三次握手即可恢复连接。零 RTT 重连&#xff1a;借助 TLS 1.3&#xff0c;实现连接恢复时的 0-RTT 数据发送&#xff08;视底层库支持&#xff09;。多…...

机器学习中的维度、过拟合、降维

1. 维度灾难 当我们谈论机器学习模型在处理数据时遇到的困难&#xff0c;一个常常被提及的词便是“维度灾难”&#xff08;Curse of Dimensionality&#xff09;。这不是科幻小说里的情节&#xff0c;而是数学和计算世界里真实存在的困境。它指的正是&#xff1a;当数据集的特…...

关于git的使用

下载git 可以去git的官网下载https://git-scm.com/downloads 也可以去找第三方的资源下载&#xff0c;下载后是一个exe应用程序&#xff0c;直接点开一直下一步就可以安装了 右键任意位置显示这两个就代表成功&#xff0c;第一个是git官方的图形化界面&#xff0c;第二个是用…...

预约按摩小程序源码介绍

基于ThinkPHP、FastAdmin和UniApp开发的预约按摩小程序源码&#xff0c;ThinkPHP作为后端框架&#xff0c;以其高效稳定著称&#xff0c;能妥善处理数据逻辑与业务规则。FastAdmin作为后台管理框架&#xff0c;极大简化了后台管理系统的搭建与维护。UniApp则让小程序具备跨平台…...

Elasticsearch创建快照仓库报错处理

创建快照仓库报错&#xff1a; 根据报错提示的信息&#xff0c;问题可能出在 Elasticsearch 的配置中。当你尝试创建一个文件系统&#xff08;fs&#xff09;类型的快照仓库时&#xff0c;虽然已经指定了 location 参数&#xff0c;但 Elasticsearch 仍然报错&#xff0c;这通…...

LINUX安装运行jeelowcode前端项目

参考 JeeLowCode低代码社区,JeeLowCode低代码开发平台,JeeLowCode低代码开发框架,快速启动&#xff08;VUE&#xff09; 安装node 18 LINUX安装node/nodejs_linux安装node 安装到哪-CSDN博客 安装PNPM LINUX安装PNPM-CSDN博客 下载 git clone https://gitcode.com/jeelo…...