Perl使用MIME :: Parser解析电子邮件正文,不包含任何部分

时间:2019-06-05 18:43:08

标签: perl email parsing mime

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

我希望能够识别电子邮件的重要部分,而不管它是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 个答案:

答案 0 :(得分:3)

对于我来说,您要实现的目标还不是很清楚,因为您仅发布代码,但没有足够详细的意图。但是您正在使用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 !~ m{inline}i;
    }
    print "body: ".$body->as_string."\n";
    last;
}