Loading... <div class="tip inlineBlock warning simple"> 🤖 本博客内容使用 GPT-4 技术进行润色 </div> 在数字化时代,数据备份不仅是我们日常工作和生活的重要环节,更是维护信息安全的关键所在。特别是对于数据库和配置文件这类经常变动的数据,自动化的更新与备份显得尤为关键。本文将引导您通过 Python 编写一个邮件自动化脚本,该脚本将能够自动将本地文件定时发送至指定邮箱,作为一种实用高效的自动化备份解决方案。 ## 一、SMTP 服务 SMTP(Simple Mail Transfer Protocol)是一种旨在提供可靠而高效电子邮件传输机制的协议。它最初是基于 FTP(File Transfer Protocol)文件传输服务构建的,专注于邮件的传递、交换以及发送方和接收方之间的通信。SMTP 的核心职责在于实现系统间的邮件信息传递,并能够提供新邮件到达的通知。 SMTP 被设计为独立于任何具体的底层传输子系统,它仅需要依赖于一个可靠和有序的数据流传输通道。SMTP 的一个重要特性是它具备的 “SMTP 邮件中继” 能力,这使得邮件能够跨越不同网络进行传输。因此,SMTP 不仅可以处理同一网络环境内的邮件传输,也能够通过中继器或网关,实现不同网络处理进程之间的邮件传递。 ### 初始化 SMTP 连接 在 Python 中,建立一个 SMTP 连接非常直接,只需几行代码。首先,我们需要导入 `smtplib`,这是处理 SMTP 协议操作的标准库。然后,我们创建一个 SMTP 对象,这将作为我们与邮件服务器沟通的主要接口。 ```python import smtplib smtpObj = smtplib.SMTP(host, port, local_hostname) ``` 参数详解: - `host`:这是你的 SMTP 服务器地址,即邮件服务提供商的服务器地址。 - `port`:当你指定了 `host` 参数后,还需要指定 SMTP 服务的端口号。通常,SMTP 的标准端口号为 25,但也可能使用其他端口如 587 或 465,具体取决于你的邮件服务商和是否使用加密。 - `local_hostname`:如果你的 SMTP 服务器运行在本地机器上,直接使用 `localhost` 作为服务器地址即可。如果你不提供这个参数,Python 会尝试获取本机的主机名。 ### 配置第三方 SMTP 服务 在大多数情况下,个人计算机并不直接运行邮件传输代理服务,如 sendmail。因此,我们通常会借助第三方邮件服务商的 SMTP 服务器来发送邮件。这些服务商包括 QQ、网易、Google 等。 以 [QQ 邮箱](https://wx.mail.qq.com/list/readtemplate?name=app_intro.html#/agreement/authorizationCode)为例,要使用它的 SMTP 服务进行邮件发送,你需要先进行一些设置,具体操作你可以查看 [QQ 邮箱获取授权码](https://service.mail.qq.com/detail/0/75)。这里是一个简单的步骤指南: 1. 登录到你的 QQ 邮箱账户。 2. 导航到设置区域,并选择 “账户” 选项。 3. 在账户设置中,找到并启用 POP3/SMTP 服务。 4. 按照页面的指示操作,并生成 16 位的授权码。 记住,使用任何第三方 SMTP 服务时,都要确保遵守服务提供商的使用条款,并正确地保护和使用你的授权码。 ## 二、发送邮件代码实现 Python 提供了一种简便的方式来自动化发送邮件过程,借助 `smtplib` 和 `email` 模块,我们可以编写脚本来发送带有附件或者图片内容的电子邮件,无需手动操作邮箱客户端。如下面实现的函数 `send_email` 所示,它封装了发送电子邮件的过程,并允许用户选择性添加附加文件或图片。 ```python # coding=utf-8 import smtplib # 用于发送邮件 import mimetypes # 用于检测附件的 MIME 类型 from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.image import MIMEImage from email.mime.base import MIMEBase from email.utils import formataddr from email import encoders from os.path import basename def send_email(sender_email, sender_password, recipient_email, email_subject, email_body='Hello World', attachment_path=None, content_type='plain'): """ 发送邮件,可以包含附件或者HTML内容。 :param sender_email: 发件人的电子邮件地址。 :param sender_password: 发件人电子邮件账户的密码。 :param recipient_email: 收件人的电子邮件地址。 :param email_subject: 邮件的主题。 :param email_body: 邮件的正文内容。 :param attachment_path: 附件文件的路径(如果有的话),默认为 None。 :param content_type: 邮件内容的类型,'plain'为普通文本,'html'为HTML内容,默认为'plain'。 """ try: # 创建一个多部分的消息对象,并设置邮件头信息 email_message = MIMEMultipart() email_message['From'] = formataddr(["发件人姓名", sender_email]) email_message['To'] = formataddr(["收件人姓名", recipient_email]) email_message['Subject'] = email_subject # 根据content_type确定邮件正文类型 email_message.attach(MIMEText(email_body, content_type, 'utf-8')) # 如果提供了附件路径,则附加文件 if attachment_path: # 猜测附件的内容类型,并相应地进行编码 content_type, encoding = mimetypes.guess_type(attachment_path) if content_type is None or encoding is not None: content_type = 'application/octet-stream' main_type, sub_type = content_type.split('/', 1) with open(attachment_path, 'rb') as file: if main_type == 'image': attachment = MIMEImage(file.read(), _subtype=sub_type) attachment.add_header('Content-ID', '<image>') else: attachment = MIMEBase(main_type, sub_type) attachment.set_payload(file.read()) encoders.encode_base64(attachment) # 添加一个头部,指示附件的文件名 attachment.add_header('Content-Disposition', 'attachment', filename=basename(attachment_path)) email_message.attach(attachment) # 登录到服务器并发送邮件 smtp_server = smtplib.SMTP_SSL("smtp.qq.com", 465) smtp_server.login(sender_email, sender_password) smtp_server.sendmail(sender_email, [recipient_email], email_message.as_string()) smtp_server.quit() print("邮件发送成功") except Exception as e: print("邮件发送失败", e) ``` 通过调用 `send_email` 函数,我们能够便捷地实现邮件的自动化发送,具体代码如下所示: ```python if __name__ == '__main__': send_email( sender_email='username@qq.com', # 发件人邮箱地址(QQ邮箱) sender_password='password', # 发件人QQ邮箱16位授权码 recipient_email="username@gmail.com", # 收件人邮箱地址 email_subject="邮件发送测试", # 邮件主题 email_body="Hello World", # 邮件正文内容 attachment_path="debug_20240229.log", # 附件文件路径,无需附件时设为None ) ``` 如果您的目标是将邮件内容转换为 HTML 格式,并确保邮件正文以 HTML 形式编码发送,您需要对邮件正文进行 HTML 格式化,并在发送邮件时将内容类型设置为 `text/html`。 ```python if __name__ == '__main__': html_content = """ <h3>This is a demo...</h3> <a href="https://www.huarzone.com">This is a link...</a> """ send_email( sender_email='username@qq.com', # 发件人邮箱地址(QQ邮箱) sender_password='password', # 发件人QQ邮箱16位授权码 recipient_email="username@gmail.com", # 收件人邮箱地址 email_subject="邮件发送测试", # 邮件主题 email_body=html_content, # 邮件正文内容 attachment_path=None, # 附件文件路径,无需附件时设为None content_type='html' # 指定发送HTML邮件 ) ``` <br> <div class="panel panel-default collapse-panel box-shadow-wrap-lg"><div class="panel-heading panel-collapse" data-toggle="collapse" data-target="#collapse-d26854a9207225e241b1cad60adf3ad611" aria-expanded="true"><div class="accordion-toggle"><span style="">分享一套不错的邮箱内容 HTML 模板</span> <i class="pull-right fontello icon-fw fontello-angle-right"></i> </div> </div> <div class="panel-body collapse-panel-body"> <div id="collapse-d26854a9207225e241b1cad60adf3ad611" class="collapse collapse-content"><p></p> ```html <table style="width:99.8%;height:99.8%"> <tbody> <tr> <td> <div style="border-radius:10px;font-size:13px;color:#555;width:666px;font-family:'Century Gothic','Trebuchet MS','Hiragino Sans GB','微软雅黑','Microsoft Yahei',Tahoma,Helvetica,Arial,SimSun,sans-serif;margin:50px auto;border:1px solid #eee;max-width:100%;background:#fff repeating-linear-gradient(-45deg,#fff,#fff 1.125rem,transparent 1.125rem,transparent 2.25rem);box-shadow:0 1px 5px rgba(0,0,0,.15)"> <div style="width:100%;background:#49BDAD;color:#fff;border-radius:10px 10px 0 0;background-image:-moz-linear-gradient(0deg,#43c6b8,#ffd1f4);background-image:-webkit-linear-gradient(0deg,#43c6b8,#ffd1f4);height:66px"><p style="font-size:15px;word-break:break-all;padding:23px 32px;margin:0;background-color:hsla(0,0%,100%,.4);border-radius:10px 10px 0 0">您的网站 [<a href="{siteUrl}" style="text-decoration:none;color:#fff" target="_blank">{siteTitle}</a>] 数据库已经更新!</p></div> <div style="margin:40px auto;width:90%"> <p> 新数据库备份文件 hash 值为: </p> <div style="background:#fafafa repeating-linear-gradient(-45deg,#fff,#fff 1.125rem,transparent 1.125rem,transparent 2.25rem);box-shadow:0 2px 5px rgba(0,0,0,.15);margin:20px 0;padding:15px;border-radius:5px;font-size:14px;color:#555">{hash}</div> <p>请注意:此邮件由 <a href="{siteUrl}" style="color:#12addb" target="_blank">{siteTitle}</a> 自动发送,请勿直接回复。</p> <p>若此邮件不是您请求的,请忽略并删除!</p> </div> </div> </td> </tr> </tbody> </table> ``` <p></p></div></div></div> <br> 由于安全性和隐私保护的原因,邮件服务提供商往往对 HTML 邮件中嵌入的外部链接施加严格的限制,通常不建议或不支持在邮件中直接加载外部资源,比如图片。为了实现在邮件 HTML 内容嵌入图片,同时确保邮件内容的可靠性与兼容性,建议采用以下方法: 1. **内联图片**:将图片转换为 Base64 编码格式,直接在 HTML 代码中内联。这样可以避免对外部资源的依赖,因为图片数据实际上被嵌入到邮件正文中。 2. **附件**:将图片作为邮件附件发送,并在 HTML 邮件中通过 CID(Content ID)引用它们。这种方法将图片作为邮件的一部分发送,而不是从外部服务器加载。 3. **可靠的托管服务**:如果必须从外部引用图片,应该选择一个可信赖的托管服务,并确保链接是 HTTPS 协议的,以增加安全性和提高图片加载的可能性。 下面的代码示例演示了如何将图片作为邮件附件发送,并在邮件正文中使用 HTML 来引用并展示这些图片: ```python if __name__ == '__main__': html_content = """ <h3>This is a demo...</h3> <a href="https://www.huarzone.com">This is a link...</a> <p>图片演示:</p> <p><img src="cid:image"></p> """ send_email( sender_email='username@qq.com', # 发件人邮箱地址(QQ邮箱) sender_password='password', # 发件人QQ邮箱16位授权码 recipient_email="username@gmail.com", # 收件人邮箱地址 email_subject="邮件发送测试", # 邮件主题 email_body=html_content, # 邮件正文内容 attachment_path="demo.png", # 附件文件路径,无需附件时设为None content_type='html' # 指定发送HTML邮件 ) ``` 若您打算在邮件中嵌入更多图片或添加其他附件,可以通过调整 `send_email` 函数来实现。这一函数可配置以支持多附件发送,您只需确保为每个附件指定正确的 Content ID(CID),然后在 HTML 内容中相应地引用它们。此外,您也可以修改函数以接受附件列表作为参数,进而在发送邮件时动态地包含多个文件。这种灵活性使得个性化邮件内容和附件管理变得简单高效。 ## 三、文件更新判断与处理 在完成邮件自动化脚本编写后,我们需要考虑如何判断数据库或配置文件是否发生更新,并据此触发邮件通知。在此处,我们仅考虑通过比较文件的哈希值来实现更新检测。原理简单理解为,计算配置文件当前的哈希值,并与之前存储的哈希值比较,如果不同则文件已更新。具体代码如下: ```python import hashlib import os # 计算文件的哈希值 def calculate_hash(filename): hasher = hashlib.sha256() with open(filename, 'rb') as file: buf = file.read() hasher.update(buf) return hasher.hexdigest() # 检查文件是否更新 def check_for_updates(filename, hash_store): current_hash = calculate_hash(filename) # 如果存储的哈希文件不存在,就认为是第一次运行 if not os.path.exists(hash_store): with open(hash_store, 'w') as file: file.write(current_hash) print("哈希存储已创建。") return False # 读取之前存储的哈希值 with open(hash_store, 'r') as file: previous_hash = file.read() # 比较当前哈希与存储的哈希 if current_hash != previous_hash: # 如果不同,更新存储的哈希值,并返回更新提醒 with open(hash_store, 'w') as file: file.write(current_hash) return True return False if __name__ == "__main__": filename = 'example.log' # 要监控的文件名 hash_store = 'file_hash.txt' # 存储哈希值的文件 if check_for_updates(filename, hash_store): """ 发送带有更新文件附件的邮件至指定邮箱 """ print(f"文件 '{filename}' 已更新,发送更新提醒。") else: print(f"文件 '{filename}' 未发生更新。") ``` ## 四、设置定时任务 最后,需要定时执行脚本,我们可以使用操作系统提供的任务计划工具。对于不同的操作系统,可以选择以下方法: 1. **对于 Windows 系统:** 使用任务计划程序(Task Scheduler)来运行 Python 脚本。 2. **对于 Unix-like 系统(包括 Linux 和 macOS):** 使用 `cron` 作业来定期执行脚本。 以下是如何在 Unix-like 系统上使用 `cron` 来定期运行 Python 脚本的步骤: 首先,打开终端并输入 `crontab -e` 命令来编辑 `cron` 作业。然后,添加一行来指定你想要如何频繁地运行脚本。例如,如果你想每天中午12点检查文件更新,你可以添加如下行: ``` 0 12 * * * /usr/bin/python3 /path/to/your/script.py ``` 在这行中,`0 12 * * *` 指定了运行时间(每天12点),`/usr/bin/python3` 是Python解释器的路径(这可能因系统而异,可以通过在终端运行 `which python3` 来找到正确的路径),`/path/to/your/script.py` 是你的 Python 脚本的路径。 如果你不熟悉 `cron` 的时间格式,这里是一个快速的参考: - `*` 表示任何值 - `0 12 * * *` 表示每天12点 - `*/10 * * * *` 表示每10分钟 - `0 */2 * * *` 表示每两小时的开始 确保在编辑 `crontab` 时保存更改。 对于 Windows 系统,你可以创建一个基本的任务计划: 1. 打开任务计划程序。 2. 创建一个基本任务,设置触发器为你想要的时间(比如每天、每周等)。 3. 作为操作,选择“启动程序”。 4. 浏览并选择你的 Python 解释器的可执行文件(通常位于 `C:\Python39\python.exe`,具体取决于Python版本和安装位置)。 5. 添加你的脚本的路径作为参数。 请确保你的 Python 脚本在没有用户交互的情况下可以运行,因为当它通过任务计划程序或 `cron` 作业运行时,它将不会有用户界面。 还要注意,如果你的脚本需要特定的环境变量或者路径设置,你可能需要在 `cron` 作业或任务计划中设置它们,或者确保脚本在其内部正确设置了它们。 Last modification:March 1, 2024 © Allow specification reprint Support Appreciate the author AliPayWeChat Like 如果觉得我的文章对你有用,请随意赞赏