Perl 使用 MIME::Parser 解析没有部分的电子邮件正文

Posted

技术标签:

【中文标题】Perl 使用 MIME::Parser 解析没有部分的电子邮件正文【英文标题】:Perl parsing email body without parts using MIME::Parser 【发布时间】:2019-10-21 07:22:33 【问题描述】:

我有一个 perl 脚本,它使用 MIME::Email 来解析从标准输入接收到的电子邮件,但它不适用于没有部分的电子邮件。我无法在电子邮件发送之前对其进行修改。

我希望能够识别电子邮件的重要部分,无论是 html 还是文本,并将其存储在缓冲区中以供以后处理。其中许多电子邮件都来自自动生成的邮件列表。

有时它们似乎只有一个“Content-Type:”标头,没有边界。

MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit

有时它们有多个文本/纯文本部分,其中一个是电子邮件的正文,另一个是签名。

这之后还有一些其他的标题行,但是正文只是显示,没有任何边界标记。

这是我两年前的帖子,展示了我是如何最终弄清楚如何用部分解析大多数电子邮件的 Parsing email with Email::MIME and multipart/mixed with subparts

use strict;
use MIME::Parser;
use MIME::Entity;
use Email::MIME;
use Email::Simple;
my $parser = MIME::Parser->new;
$parser->extract_uuencode(1);
$parser->extract_nested_messages(1);
$parser->output_to_core(1);
my $buf;
while(<STDIN> )
        $buf .= $_; 


my $entity = $parser->parse_data($buf);

$entity->dump_skeleton;
my $num_parts = $entity->parts;
for (my $i=0; $i < $num_parts; $i++) 
    my $part = $entity->parts($i);
    my $content_type = $part->mime_type;
    my $body = $part->as_string;

    print "body: $body\n";

从不打印正文。仅来自 dump_skeleton 的以下内容:

Content-type: text/plain
Effective-type: text/plain
Body-file: NONE
Subject: Security update 

我真的很希望能够修改我现有的脚本(在上一篇 stackexchange 帖子中显示),以便能够无边界地打印这样的电子邮件。

这是糟糕的格式吗?如果没有复杂的步骤来按部分分析整个邮件,我一直无法找到任何可用于可靠地打印电子邮件的正文、主题和其他基本标题的库示例。

我知道 mimeexplode 可以做到,但我不知道怎么做。我需要将邮件正文存储在缓冲区中以进行操作,因此使用像 mimeexplode 这样的命令行程序无论如何都是一种迂回的方式。

【问题讨论】:

【参考方案1】:

我并不完全清楚你想要实现什么,因为你只发布了代码,但没有足够详细地发布代码背后的意图。但是您使用parts 来检查clearly documented 的消息以返回multipart/* 或类似的部分(即message/rfc822)并且不处理单个消息:

...返回所有子部分的数组,如果没有则返回空数组(例如,如果这是单部分消息,或退化的多部分)。在标量上下文中,这会返回部分的数量。

如果您只想获取所有部分,包括独立的“部分”(即不属于任何内容的单个实体),只需使用parts_DFS,如下例所示,它会打印所有具有非-零体:

use MIME::Parser;
my $parser = MIME::Parser->new;
my $entity = $parser->parse(\*STDIN);
for my $part ($entity->parts_DFS) 
    defined(my $body = $part->bodyhandle) or next; # has no body, likely multipart or similar
    print "body: ".$body->as_string."\n";

编辑:鉴于您已更新问题,您不是在寻找所有部分,而是在寻找正文部分。很难确定实际的 main 部分是什么,但您可以尝试使用第一个内联的 text/* 部分。这可能看起来像这样:

use MIME::Parser;
my $parser = MIME::Parser->new;
my $entity = $parser->parse(\*STDIN);
for my $part ($entity->parts_DFS) 
    defined(my $body = $part->bodyhandle) or next; # has no body, likely multipart or similar
    if (my $disp = $part->head->get('content-disposition')) 
        next if $disp !~ minlinei;
    
    print "body: ".$body->as_string."\n";
    last;

【讨论】:

好的,这似乎可行,但你能解开它以使其更容易理解吗?我了解有关创建新实例和从 STDIN 读取的部分,但是当您创建 MIME::Parser 对象的新实例时,parts_DFS 似乎是 MIME::Entity 的一部分。车身把手是干什么用的?你可以调整它以使用我之前帖子中检查消息部分的功能吗? @AlexRegan:正如我所说,我不清楚您首先要实现的目标:您只发布代码而没有解释它的意图,并且唯一抱怨的是它不处理非多部分消息。因此,我通过使用 parts_DFS 而不是 parts 来关注您所抱怨的内容。我已经更改了示例代码,至少现在更容易理解。与您的示例代码相比,$body 实际上包含 MIME 主体(即变量名称和内容匹配)而不是整个部分,包括。 MIME 标头。 感谢您的帮助。我现在才回到这个话题。我已经运行了你的代码,现在明白为什么它不清楚了。我希望只识别和管理实际电子邮件的内容,而不是多部分或其他组件,如 PGP 签名等。我最终将解析电子邮件并将其部分存储在数据库中。我已经更新了帖子以希望澄清。

以上是关于Perl 使用 MIME::Parser 解析没有部分的电子邮件正文的主要内容,如果未能解决你的问题,请参考以下文章

Perl 解析 Outlook 收件箱中的电子邮件和附件

使用 MIME::Parser 拆分电子邮件

在 ./MIME/Parser/Filer.pm 第 365 行调用未定义的子例程 utf8::SWASHGET

perl 中的 HTML 解析

解析Perl 5并检查语法树

是否存在用于 rakudo 的 Perl 6 模块来解析 MIME? [关闭]