如何将 msg 中的附件附加到 Mime 以在 Python 中作为电子邮件发送?

Posted

技术标签:

【中文标题】如何将 msg 中的附件附加到 Mime 以在 Python 中作为电子邮件发送?【英文标题】:How do I attach an attachment from msg to a Mime to send as email in Python? 【发布时间】:2021-10-13 06:48:15 【问题描述】:

我想打开一个.msg 文件,获取附件,然后将它们发送到特定的邮件地址。 我使用的是extract_msg,我似乎无法仅将.msg 中的附件附加到我的邮件中。

到目前为止我所拥有的:

mail = f"path/attachment"
msg = extract_msg.Message(mail)
msg_sender = str(msg.sender)
msg_subj = str(msg.subject)
msg_message = str(msg.body)

email_sender = 'automailsender123@gmail.com'
password = 'XXX'
send_to_email = send_to
print("----------------------------------------------------------------------------------------------------")
print(f"Send to: send_to\nAttachement: attachment\n")

msgMime = MIMEMultipart()
msgMime['From'] = msg_sender
msgMime['To'] = send_to_email
msgMime['Subject'] = msg_subj
body = MIMEText(msg_message)
msgMime.attach(body)

resources_dir = "resources"
attachments_dir = os.path.join(resources_dir, "attachments")
part = MIMEBase('application', 'octet-stream')

if msg.attachments:
    with tempfile.TemporaryDirectory() as tmp_dir_name:
        for att in msg.attachments:
            att_save_path = os.path.join(tmp_dir_name, att.longFilename)
            att.save(customPath=tmp_dir_name)

            attachment_stream = open(att_save_path, 'rb')
            part.set_payload(attachment_stream.read())
            encoders.encode_base64(part)
            part.add_header('Content-Disposition', "attachment; filename= %s" % att_save_path)
            msgMime.attach(part)

else: print("No attachments")

server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
try:
    server.login(email_sender, password)
except:
    print(f"Login failed for user: email_sender\nWith password: password")

text = msgMime.as_string()
server.sendmail(email_sender, send_to_email, text)
server.quit()

我得到错误:

     File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\shutil.py", line 596, in _rmtree_unsafe
    with os.scandir(path) as scandir_it:
NotADirectoryError: [WinError 267] The directory name is invalid: 'C:\\Users\\XXX.XXX\\AppData\\Local\\Temp\\tmpguckqnkr\\image001.jpg'

我写信给 temp 的唯一原因是,将原始 .msg 的附件附加到 Mime 似乎是不可能的。

欢迎任何帮助,包括错误或处理附件的更好方法。

编辑: 堆栈跟踪:

Traceback (most recent call last):
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\shutil.py", line 616, in _rmtree_unsafe
    os.unlink(fullname)
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\KENNET~1.VAN\\AppData\\Local\\Temp\\tmpguckqnkr\\image001.jpg'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\tempfile.py", line 801, in onerror
    _os.unlink(path)
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\KENNET~1.VAN\\AppData\\Local\\Temp\\tmpguckqnkr\\image001.jpg'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\tempfile.py", line 804, in onerror
    cls._rmtree(path)
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\tempfile.py", line 812, in _rmtree
    _shutil.rmtree(name, onerror=onerror)
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\shutil.py", line 740, in rmtree
    return _rmtree_unsafe(path, onerror)
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\shutil.py", line 599, in _rmtree_unsafe
    onerror(os.scandir, path, sys.exc_info())
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\shutil.py", line 596, in _rmtree_unsafe
    with os.scandir(path) as scandir_it:
NotADirectoryError: [WinError 267] The directory name is invalid: 'C:\\Users\\KENNET~1.VAN\\AppData\\Local\\Temp\\tmpguckqnkr\\image001.jpg'

【问题讨论】:

似乎 att.save(customPath=tmp_dir_name) 尝试保存到文件夹名称,而不是文件名。 顺便说一句。看起来您的 email 代码是为较旧的 Python 版本编写的。标准库中的email 模块在 Python 3.6 中进行了彻底改造,使其更加合乎逻辑、通用性和简洁性;新代码应该针对(不再是非常)新的EmailMessage API。可能扔掉这段代码,从the Python email examples documentation.的现代代码重新开始 msg 是指 Outlook 吗?您的那部分代码不清楚;请提供minimal reproducible example。 这是课程的一部分吗?这就像两天内的第四个类似问题。 请提供完整的追溯。您自己的代码中没有os.scandir,我们无法猜测是哪一行触发了这个错误。 【参考方案1】:

好吧,伙计们,这就是我的工作方式:

def send_mails_from_msg(attachment, send_to, path):
  mail = f"path/attachment"
  msg = extract_msg.Message(mail)

  msg_sender = str(msg.sender)
  msg_subj = str(msg.subject)
  msg_message = str(msg.body)
  email_sender = 'mailsender123@gmail.com'
  password = 'XXXXXX'

  print("----------------------------------------------------------------------------------------------------")
  print(f"Send to: send_to\nAttachement: attachment\n")

  msgMime = MIMEMultipart()
  msgMime['From'] = msg_sender
  msgMime['To'] = send_to
  msgMime['Subject'] = msg_subj
  body = MIMEText(msg_message)
  msgMime.attach(body)

  resources_dir = "resources"
  attachments_dir = os.path.join(resources_dir, "attachments")
  part = MIMEBase('application', 'octet-stream')

  if msg.attachments:
      for x in msg.attachments:
          x.save(customPath=attachments_dir)
      for files in os.listdir(attachments_dir):
          print(f"Files: files")
          attachment = open(f"attachments_dir/files", 'rb')
          part.set_payload(attachment.read())
          encoders.encode_base64(part)
          part.add_header('Content-Disposition', "attachment; 

文件名= %s" % 附件) msgMime.attach(部分)

  else: print("No attachments")

  server = smtplib.SMTP('smtp.gmail.com', 587)
  server.starttls()
  try:
      server.login(email_sender, password)
  except:
      print(f"Login failed for user: email_sender\nWith password: password")

  text = msgMime.as_string()
  server.sendmail(email_sender, send_to, text)
  server.quit()

现在这会发送带有邮件的 jpg,任务完成! 但.... 附件是这样的:__io.BufferedReader name='resources_attachments_image001.jpg'_,注册为附件但不能openend... 我现在正在尝试解决这个问题

【讨论】:

附件本身很好,但是您传递"attachment; filename= %s" % attachment 可能是指'attachment; filename="%s"' % filesfiles 作为文件名(可能重命名为单数)和MIME 关键字的正确格式(等号后没有空格;文件名两边加双引号)。【参考方案2】:

目前尚不清楚Message() 部分来自何处或它究竟做了什么。我将假设它按您希望的那样工作,并推测您可能是 Outlook 受害者,他有幸找到了一个可以可接受地操纵其专有消息的库。

您的代码有两个有问题的流程:即使没有提取附件,它也会发送电子邮件,即使连接到 SMTP 服务器失败,它也会尝试发送。您想将这些重构为类似

else:
    print("No attachments")
    sys.exit(0)

在没有提取任何内容时退出,并且

try:
    server.login(email_sender, password)
except:
    print(f"Login failed for user: email_sender\nWith password: password")
    sys.exit(1)

当您无法继续时退出并出现错误(或者只是取出 try / except 并让脚本崩溃并回溯;如果它仅供您个人使用,这可能是可以接受的,并且看到调试信息就更容易调试了)。

(不是一个正确的答案,但我觉得我也需要指出这些问题。)

【讨论】:

谢谢,完全正确! Message() 来自 extract_msg 库。在我的用例中,在没有附件的情况下发送电子邮件不是问题,我只需要确保它何时也有发送的附件。但是您的第二句话非常中肯,感谢您指出。

以上是关于如何将 msg 中的附件附加到 Mime 以在 Python 中作为电子邮件发送?的主要内容,如果未能解决你的问题,请参考以下文章

python MIME将多个附件附加到多部分消息

如何编写多部分 MIME 混合消息以在 Outlook 中正确显示

MIME::Lite 附加文件 perl 时出错

将一些文件附加到 MIME 多部分电子邮件

python MIME:如何更改附件的名称?

使用 PHP 的邮件正文中的 MIME 信息