Python 之 Email 库详解

469 阅读5分钟

Python 之 Email 库详解

Python 标准库中提供了强大的 email 模块,可以帮助我们轻松地处理电子邮件。本文将详细介绍如何使用 Python 的 email 库来创建、解析和发送电子邮件。

一、安装与导入

首先,email 库是 Python 标准库的一部分,因此无需额外安装。我们可以直接导入所需的模块:

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders

二、创建电子邮件

使用 email 模块,我们可以创建简单的文本邮件、包含附件的邮件等。

1. 创建简单文本邮件

以下代码展示了如何创建一个简单的文本电子邮件:

# 设置邮件内容
msg = MIMEText('这是邮件的正文内容', 'plain', 'utf-8')

# 设置邮件主题、发件人、收件人
msg['Subject'] = '邮件主题'
msg['From'] = 'your_email@example.com'
msg['To'] = 'recipient_email@example.com'
2. 创建包含附件的邮件

如果我们需要发送包含附件的邮件,可以使用 MIMEMultipart 组合多个部分:

# 创建MIMEMultipart对象
msg = MIMEMultipart()
msg['Subject'] = '带附件的邮件'
msg['From'] = 'your_email@example.com'
msg['To'] = 'recipient_email@example.com'

# 邮件正文
body = MIMEText('这是带附件的邮件的正文部分', 'plain', 'utf-8')
msg.attach(body)

# 添加附件
filename = 'example.pdf'
with open(filename, 'rb') as attachment:
    part = MIMEBase('application', 'octet-stream')
    part.set_payload(attachment.read())

# 编码附件
encoders.encode_base64(part)
part.add_header('Content-Disposition', f'attachment; filename={filename}')
msg.attach(part)

三、发送电子邮件

创建好电子邮件后,我们需要使用 smtplib 模块发送邮件。

# 连接到SMTP服务器
smtp_server = 'smtp.example.com'
smtp_port = 587
username = 'your_email@example.com'
password = 'your_password'

with smtplib.SMTP(smtp_server, smtp_port) as server:
    server.starttls()  # 启用TLS加密
    server.login(username, password)
    server.sendmail(msg['From'], msg['To'], msg.as_string())

四、解析电子邮件

除了创建和发送邮件,我们还可以使用 email 模块来解析收到的电子邮件。

class EmailUtil:
    """
    Email公共方法
    """

    imap = None

    def __init__(self, host, port):
        """
        初始化方法
        :param host: 域名
        :param port: 端口
        """
        self.host = host
        self.port = port
        self.imap = imaplib.IMAP4_SSL(host=self.host, port=int(self.port))

    def login(self, username, password):
        """
        登录邮箱
        :param username: 用户名
        :param password: 密码
        :return:
        """
        self.username = username
        self.password = password
        self.imap.login(user=self.username, password=self.password)

    def get_unsee_mail(self):
        """
        获取未读邮件ID
        :return:
        """
        self.imap.select("inbox", readonly=False)

        result, email_ids = self.imap.search(None, 'UNSEEN')
        email_ids = email_ids[0].split()
        return email_ids

    def get_all_mail(self):
        """
        获取所有邮件ID
        :return:
        """
        self.imap.select("inbox", readonly=False)
        # 搜索电子邮件
        status, email_ids = self.imap.search(None, "ALL")
        email_ids = email_ids[0].split()
        return email_ids

    def get_email_content(self, num):
        # 通过邮件编号获取邮件内容
        typ1, msg_data = self.imap.fetch(num, "(RFC822)")
        raw_email = msg_data[0][1]

        if typ1 == 'OK':
            # 将原始的邮件数据解析为邮件对象
            msg = email.message_from_bytes(raw_email)
            print(msg)
            # 解析并打印邮件的各个部分
            # 获取邮件的发件人
            sender = msg['From']
            # 解析发件人字符串以获取电子邮件地址和显示名称
            realname, email_address = parseaddr(sender)
            sender = email_address
            print(realname,email_address)
            if realname:
                decoded_realname, charset = decode_header(realname)[0]
                if charset:
                    realname = decoded_realname.decode(charset)
                else:
                    realname = decoded_realname  # 如果charset为None,则可能是字节串或已经是str

            print('From:', email_address)
            if realname:
                print('Display Name:', realname)


            # 获取邮件的收件人
            to = msg['To']
            to, charset = decode_header(to)[0]
            if charset:
                to = to.decode(charset)
            print('To:', to)

            # 获取邮件的主题
            if 'Subject' in msg:

                subject, encoding = decode_header(msg['Subject'])[0]
                if encoding:
                    subject = subject.decode(encoding, errors='replace')

            else:
                subject = ''

            print('Subject:', subject)

            # 获取邮件的日期
            email_date = msg['Date']
            print("email_date", email_date)
            # 使用email.utils解析日期字符串和时间区信息
            parsed_date = email.utils.parsedate_tz(email_date)

            # 检查是否解析成功
            if parsed_date is not None:
                # 提取日期和时间信息(不包括时区)
                time_tuple = parsed_date[:9]

                # 创建一个naive datetime对象(即没有时区信息的datetime对象)
                naive_dt = datetime(*time_tuple[:6])
                naive_dt = naive_dt.replace(microsecond=time_tuple[6])

                # 提取时区偏移量(单位为秒)
                tz_offset = parsed_date[9]

                # 创建一个带有时区信息的datetime对象
                aware_dt = naive_dt.replace(tzinfo=timezone(timedelta(seconds=tz_offset)))

                # 转换到上海时区
                shanghai_tz = pytz.timezone('Asia/Shanghai')
                shanghai_dt = aware_dt.astimezone(shanghai_tz).strftime("%Y-%m-%d %H:%M:%S")

                # 打印上海时区的时间
                print(shanghai_dt)
            else:
                print("无法解析日期字符串")
            print('Date:', shanghai_dt)
            # 初始化一个字符串来保存按顺序拼接的内容
            pure_text_content = ""
            # 遍历邮件的所有部分(包括附件)
            for part in msg.walk():
                # 忽略附件
                if part.get_content_type() == 'multipart':
                    continue
                # elif part.get_content_type()=='image/jpeg':
                #     print("该内容是图片")

                # 如果是文本部分
                elif part.get_content_type() == 'text/plain':
                    body = part.get_payload(decode=True)
                    pure_text_content += body.decode('utf-8', errors='replace')
                    # print('Text body:', body.decode('utf-8',errors='replace'))

                    # 如果是HTML部分
                elif part.get_content_type() == 'text/html':
                    body = part.get_payload(decode=True)
                    if part.get('Content-Transfer-Encoding') == 'base64':
                        body = body.decode('utf-8')
                    soup = BeautifulSoup(body, 'html.parser')
                    if soup.find('body'):
                        body_tag = soup.body
                        # 遍历 <body> 标签的所有子节点
                        for child in body_tag.contents:
                            # 检查 child 是否是 NavigableString(即文本节点)
                            if isinstance(child, NavigableString):

                                # 去除字符串前后的空白字符并添加到拼接字符串中
                                stripped_text = child.strip()
                                if stripped_text:
                                    if stripped_text not in pure_text_content:
                                        pure_text_content += stripped_text
                                    # 检查 child 是否是 Tag 并且标签名为 'div'
                            elif isinstance(child, Tag) and child.name == 'div':
                                # 如果是 div 标签,获取其内部的纯文本内容并添加到拼接字符串中
                                if child.get_text(strip=True) not in pure_text_content:
                                    pure_text_content += child.get_text(strip=True)
                                    pure_text_content += "\n"
                                # 如果 div 后面紧跟着文本节点,可能需要添加换行或空格来分隔内容
                                # 这里可以根据你的需求决定是否添加
                    else:
                        for div in soup.find_all('div'):
                            # 提取<div>的内容(只提取文本内容,不包括标签)
                            if div.get_text(strip=True,
                                            separator=" ") not in pure_text_content:
                                pure_text_content += div.get_text(strip=True,
                                                                  separator=" ")  # strip=True去除每段文本前后的空白,separator指定文本间的分隔符
                                # 将当前<div>的内容添加到总字符串中
                                pure_text_content += "\n"  # 可以根据需要添加换行符或其他分隔符

            print("邮件内容:", pure_text_content)
            # 检查邮件标志
            _, flags_data = self.imap.fetch(num, '(FLAGS)')
            flags = flags_data[0].decode('utf-8')
            # 检查邮件是否已读(\Seen)和是否已回复(\Answered)
            print(flags)

            if '\Answered' in flags:
                email_status = 'Answered'
            elif '\Seen' in flags:
                email_status = 'Seen'
            else:
                email_status = 'Unseen'

            print(email_status)
          
            return num, sender, to, shanghai_dt, subject, pure_text_content,email_status

五、总结

Python 的 email 库提供了丰富的功能,可以帮助我们轻松地创建、发送和解析电子邮件。通过本文的介绍,希望大家能够更好地理解和使用这个强大的工具。无论是简单的文本邮件,还是复杂的带附件的邮件,都可以通过 email 库轻松实现。

如果你对本文有任何疑问或建议,欢迎在评论区留言讨论。