如何使用 Python 在现有的 eml 文件中添加 pdf 附件?

How to add pdf attachment in an existing eml file using Python?

目前我正在处理 eml 文件,我最近正在处理这些类型的文件:

我不得不更改发件人姓名并将相同的 eml 文件发送给那些发件人,但我想将 .eml 文件添加到我现有的

我已经通过 email.parser 库使用替换 header 命令成功更改了发件人。

例如:

from email.parser import Parser
f = open('C:\Users\Downloads\Message.eml', 'r+')
header = Parser().parse(f)

headers.replace_header(headername, headervalue)
headers.replace_header('to', 'name@gmail.com') 

但现在我不知道如何在同一 .eml 文件的电子邮件 body 中添加 pdf 附件。以下是 .eml 文件的示例。在此我想添加一个附件。

你们能帮忙吗?

.eml 文件模板:

Delivered-To: ***@gmail.com
Received: by 2002:ac9:1e03:0:0:0:0:0 with SMTP id r3csp1380999oci;
        Thu, 6 May 2021 03:24:14 -0700 (PDT)
X-Google-Smtp-Source: ABdhPJxznI2eQK4UcAUk1vJbeKdYMovRwYMwxz4trgXWy7O+V1jklccpi92jvFWplswmqnBJfdpV
X-Received: by 2002:a17:2:94:b9:ec:7fd5:193e with SMTP id w4-20020a1709029a84b02900ec7fd5193emr3638953plp.62.1620296654743;
        Thu, 06 May 2021 03:24:14 -0700 (PDT)
ARC-Seal: i=1; a=rsa-sha256; t=1620296654; cv=none;
        d=google.com; s=arc-20160816;
        b=EH7EGZH8A3o9/LvvqIgO3KaZPU82Jn0iX0/kGV5W/tawujBF7y3qV3Er4lpFtX
         rm1jiy+cH3CPEHEiAyyd3XSuBZFA+AoE8xpoZxXaTxmqB6vBQXVWigVUUTKcsl71CSVs
         xLG7NHWsFABWEdemJY/cnibY85tpk1NpVISzDihAd4IShMKOGlYqoOlyWf06pdyIc2y6
         DZVYrlo/oWsnD2VT5nYiVqMeOwjUKIVg9ACyZIIRpmMQT/2/lutcsrLPMBBJbLK1vpgU
         jpZHu3s++EFPjmuTijNbyvv/5d5RrcsOwvLpWqk
         8U1A==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
        h=from:subject:sender:message-id:to:mime-version:date:dkim-signature;
        bh=oz3nVaiXvPhENQytolVf3ACAgfI2p8aslAq1BN/w55M=;
        b=fUeNvuOk3JjseXNpa+wFWtdmRjgG/Le5G62cV0ZMbelccGKi1H7GWx
         Exred4q9phvSSGV7ZuE+U5MXpwL1tXmPYZhHO+fj5uPEt6dY2x
         Yqg2/1IxDhcd/3NLH8CB19AolyRgAA8Qn+ThyBgpHs8mCVQ0f5XzxZvP/rKf
         WXxyQwA/1CcOPEcDlaOPAZNngacjvxeecjWWLrHUK1eH
         bETcDxabCPKXagRnP4xDXwTSqzj4Gtjsbc7 +v
         WGfw==
ARC-Authentication-Results: i=1; mx.google.com;
       dkim=pass header.i=@mail.com header.s=mail header.b=LRDuMb9q;
       spf=pass (google.com: domain of ***@mail.com designates ****.137.**.*** as permitted sender) smtp.mailfrom=***@mail.com;
       dmarc=pass (p=QUARANTINE sp=REJECT dis=NONE) header.from=mail.com
Return-Path: <***@mail.com>
Received: from mail.com (f4mail-235-203. mail.com. [***.137.**.***])
        by mx.google.com with ESMTPS id l10si2328115pgb.331.2021.05.06.03.24.13
        for <xxxxx@gmail.com>
        (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128);
        Thu, 06 May 2021 03:24:14 -0700 (PDT)
Received-SPF: pass (google.com: domain of ***@mail.com designates ***.137.**.*** as permitted sender) client-ip=***.137.**.***;
Authentication-Results: mx.google.com;
       dkim=pass header.i=@mail.com header.s=mail header.b=LRDuMb9q;
       spf=pass (google.com: domain of ***@mail.com designates ***.137.**.*** as permitted sender) smtp.mailfrom=***@mail.com;
       dmarc=pass (p=QUARANTINE sp=REJECT dis=NONE) header.from=mail.com
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mail.com;
    s=mail; t=1620296652;
    bh=oz3nVaiXvPhENQytolVf3ACAgfI2p8aslAq1BN/w55M=;
    h=MIME-Version:From:Date:Message-ID:Subject:To:Content-Type;
    b=LRDuMb9qOWYq/u397M6T9zLkk1kInTolxD538xl5crHBsb3PL8eR5GiE0Deg7fTNe
     T8+whLVLTServKQLpxrEE3ob/6c5gr11SFYP8dIyzYU+qhbtxp6OJcAnBuxkJSRgRD
     JFQ/6oaHO49Jhz/2qkQ82USjrCi1fiAZe/mBKUGY=
Received: (qmail 20965 invoked by uid 510); 6 May 2021 10:24:12 -0000
x-m-msg: asd54ad564ad7aa6sd5as6d5; a6da7d6asas6dasd77; 5dad65ad5sd;
X-OUT-VDRT-SpamState: 0\LEGIT
X-OUT-VDRT-SpamScore: 0
X-OUT-VDRT-SpamCause: gggruggvucftvghtrhhoucdtuddrgeduledrvdegtddgvdejucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecufdftgfffkffhhfdpqfgfvfdfnecuuegrihhlohhuthemuceftddtnecunecujfgurhepffggvffkshfuhfgtsegrtderredttdejnecuhfhrohhmpedfrfhrihihrghnkhgrucffvghsrghifdcuoehpihihrgguvghsrghiudduuddusehrvgguihhffhhmrghilhdrtghomheqnecuggftrfgrthhtvghrnheptdefkeehkeduhfeljeelleehgefgffeutdeljedtiedtgeeigfdtjeettedvkedtnecukfhppedurddukeeirdduvdegrdduheeinecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmohguvgepshhmthhpohhuth
X-Remote-IP: ***.137.**.***
X-REDF-OSEN: ***@mail.com
Date: 6 May 2021 10:24:12 -0000
MIME-Version: 1.0
To: "***" <***@gmail.com>
Received: from unknown ***.137.**.*** by mail.com via HTTP; 06 May 2021 10:24:12 -0000
X-Senderscore: D=0&S=0
Message-ID: <1620296512.S.1386.3658.f4mail-***-13*.mail.com.1620296652.20941@webmail.mail.com>
Sender: ***@mail.com
Subject: =?utf-8?B?UmVxdWVzdGVkIGRvY3VtZW50cw==?=
From: "Fisrtname Lastname" <***@mail.com>
Content-Type: multipart/alternative;
    boundary="=_f619e79a5c2c1319e417d1bc96f343f8"

--=_f619e79a5c2c1319e417d1bc96f343f8
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="UTF-8"



Hey Name,

I hope you are fine and staying safe.
&nbsp;Please find the attached document of some details for the whole process for this program.&nbsp; Once you go through it if you find interest to know then let&#39;s have a discussion.

Regards,
Name LastName
&nbsp;
--=_f619e79a5c2c1319e417d1bc96f343f8
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset="UTF-8"

<br /><br />Hey Name,<br /><br />I hope you are fine and staying safe.<br /=
>&nbsp;<div>Please find the attached document of some details for the whole=
 process for this program.&nbsp; Once you go through it if you find interes=
t to know then let&#39;s have a discussion.<br /><br />Regards,<br />Name L=
astName<br />&nbsp;</div><br>
--=_f619e79a5c2c1319e417d1bc96f343f8--

您使用的是旧版 email.message.Message API,它不容易让您将附件添加到已解析的邮件中。但切换到现代 Python 3.6+ API 很容易,总体上是个好主意,这使它成为 breeze.

有点晦涩,使 email.parser 模块生成现代 EmailMessage object 而不是遗留 Message 的方法是传入 policy 参数.

顺便说一句,如果电子邮件在磁盘上不是纯 UTF-8,您应该将文件读取为 bytes 并使用 BytesParser 以避免出现奇怪的编码错误。许多 real-world 邮件包含 Latin-1 或各种亚洲字符集;但正确的解决方案是甚至不要尝试猜测 - 只需将其作为二进制 blob 读入并让 email 解析器计算出来。

from email.parser import BytesParser
from email.policy import default

headername = 'sender'
headervalue = 'you need to show us your variables <failure@example.net>'

with open(r'C:\Users\Sun\Download\Message.eml', 'rb') as mess:
    message = BytesParser(policy=default).parse(mess)

message.replace_header(headername, headervalue)
message.replace_header('to', 'name@gmail.com')

message.add_attachment(b'\x00\x00\x00', 'attachment', 'pdf', filename='poop.pdf')

add_attachment 方法的文档也有点少;它的参数是附件 body 数据、主要类型和子类型(尽管您可以添加其他参数,例如配置,以便为收件人提供文件名)。参见例如Addng attachment to an EmailMessage raises TypeError: set_text_content() got an unexpected keyword argument 'maintype'

通常,您可能还想从文件中读取 PDF:

from pathlib import Path

pdf_file = Path(r'C:\windows\horrible\atrocious\nasty\file.pdf')
with pdf_file.open('rb') as pdf:
    message.add_attachment(
        pdf.read(), 'attachment', 'pdf', filename=pdf_file.name)

注意我们如何再次需要二进制输入,以及 message.add_attachment 的第一个参数是如何包含有效负载的 bytes object。

这里的pathlib转换并不是绝对必要的,而是展示了如何只输入一次文件名,并将其传递给add_attachmentfilename关键字参数以使用那里有相同的文件名,没有目录部分。

如果要将修改后的消息写入同一个文件,只需 close 文件句柄(或者更好,在最初读取时使用 with open() ...:,这样您就不必记住明确地close(),就像我上面做的那样)并打开阅读,然后写出新内容。再次提醒,记得用二进制方式,写出bytes.

with open(filename, 'wb') as destination:
    destination.write(message.as_bytes())

模糊改编的演示:https://ideone.com/NBMOs1

顺便说一句,您的 SMTP 服务器不关心 To: header 中的内容;如果你 sendmail me@example.org <Message.eml 它将交付给我,而不是 To: header 中的任何人。 (sendmail 并不常安装在 Windows 上,但同样的原则也适用。)如果你担心的话,也许你实际上并不想要或不需要更换 To: header .