Python: Cách phân tích cú pháp Phần thân từ một email thô, vì email thô không có thẻ “Nội dung” hoặc bất kỳ thứ gì


81

Có vẻ như dễ dàng để có được

From
To
Subject

vv qua

import email
b = email.message_from_string(a)
bbb = b['from']
ccc = b['to']

giả sử đó "a"là chuỗi email thô trông giống như thế này.

a = """From root@a1.local.tld Thu Jul 25 19:28:59 2013
Received: from a1.local.tld (localhost [127.0.0.1])
    by a1.local.tld (8.14.4/8.14.4) with ESMTP id r6Q2SxeQ003866
    for <ooo@a1.local.tld>; Thu, 25 Jul 2013 19:28:59 -0700
Received: (from root@localhost)
    by a1.local.tld (8.14.4/8.14.4/Submit) id r6Q2Sxbh003865;
    Thu, 25 Jul 2013 19:28:59 -0700
From: root@a1.local.tld
Subject: oooooooooooooooo
To: ooo@a1.local.tld
Cc: 
X-Originating-IP: 192.168.15.127
X-Mailer: Webmin 1.420
Message-Id: <1374805739.3861@a1>
Date: Thu, 25 Jul 2013 19:28:59 -0700 (PDT)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="bound1374805739"

This is a multi-part message in MIME format.

--bound1374805739
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

ooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooo

--bound1374805739--"""

CÂU HỎI

Làm thế nào để bạn nhận được Bodyemail này qua python?

Cho đến nay, đây là mã duy nhất tôi biết nhưng tôi vẫn chưa kiểm tra nó.

if email.is_multipart():
    for part in email.get_payload():
        print part.get_payload()
else:
    print email.get_payload()

đây có phải là cách chính xác?

hoặc có thể có một cái gì đó đơn giản hơn như ...

import email
b = email.message_from_string(a)
bbb = b['body']

?

Câu trả lời:


94

Sử dụng Message.get_payload

b = email.message_from_string(a)
if b.is_multipart():
    for payload in b.get_payload():
        # if payload.is_multipart(): ...
        print payload.get_payload()
else:
    print b.get_payload()

111

Để có tính tích cực cao, bạn làm việc với nội dung email thực tế (tuy nhiên, vẫn có khả năng bạn không phân tích cú pháp đúng phần), bạn phải bỏ qua tệp đính kèm và tập trung vào phần đơn giản hoặc html (tùy thuộc vào nhu cầu của bạn) để biết thêm Chế biến.

Vì các tệp đính kèm được đề cập trước đó có thể và rất thường là phần văn bản / thuần túy hoặc văn bản / html, mẫu không có gạch đầu dòng này bỏ qua các tệp đó bằng cách kiểm tra tiêu đề bố trí nội dung:

b = email.message_from_string(a)
body = ""

if b.is_multipart():
    for part in b.walk():
        ctype = part.get_content_type()
        cdispo = str(part.get('Content-Disposition'))

        # skip any text/plain (txt) attachments
        if ctype == 'text/plain' and 'attachment' not in cdispo:
            body = part.get_payload(decode=True)  # decode
            break
# not multipart - i.e. plain text, no attachments, keeping fingers crossed
else:
    body = b.get_payload(decode=True)

BTW, walk()lặp lại kỳ diệu trên các phần kịch câm và get_payload(decode=True)thực hiện công việc bẩn thỉu về giải mã base64, v.v. cho bạn.

Một số thông tin cơ bản - như tôi đã ngụ ý, thế giới tuyệt vời của email MIME thể hiện rất nhiều cạm bẫy khi "tìm nhầm" nội dung thư. Trong trường hợp đơn giản nhất, nó nằm trong phần "văn bản / đơn giản" duy nhất và get_payload () rất hấp dẫn, nhưng chúng ta không sống trong một thế giới đơn giản - nó thường được bao quanh trong nội dung nhiều phần / thay thế, liên quan, hỗn hợp, v.v. Wikipedia mô tả nó chặt chẽ - MIME , nhưng xem xét tất cả các trường hợp dưới đây là hợp lệ - và phổ biến - người ta phải xem xét mạng lưới an toàn xung quanh:

Rất phổ biến - hầu hết những gì bạn nhận được trong trình soạn thảo thông thường (Gmail, Outlook) gửi văn bản có định dạng với tệp đính kèm:

multipart/mixed
 |
 +- multipart/related
 |   |
 |   +- multipart/alternative
 |   |   |
 |   |   +- text/plain
 |   |   +- text/html
 |   |      
 |   +- image/png
 |
 +-- application/msexcel

Tương đối đơn giản - chỉ là đại diện thay thế:

multipart/alternative
 |
 +- text/plain
 +- text/html

Đối với tốt hay xấu, cấu trúc này cũng hợp lệ:

multipart/alternative
 |
 +- text/plain
 +- multipart/related
      |
      +- text/html
      +- image/jpeg

Mong cái này giúp được chút ít.

PS Quan điểm của tôi là đừng tiếp cận email một cách nhẹ nhàng - nó cắn khi bạn ít mong đợi nhất :)


5
Cảm ơn vì ví dụ kỹ lưỡng này và vì đã đánh vần một cảnh báo - ngược lại với câu trả lời được chấp nhận. Tôi nghĩ đây là một cách tiếp cận tốt hơn / an toàn hơn nhiều.
Simon Steinberger

1
Ah, rất tốt! .get_payload(decode=True)thay vì chỉ .get_payload()làm cho cuộc sống dễ dàng hơn nhiều, cảm ơn!
Đánh dấu

9

Có rất tốt sẵn gói để phân tích nội dung email với tài liệu thích hợp.

import mailparser

mail = mailparser.parse_from_file(f)
mail = mailparser.parse_from_file_obj(fp)
mail = mailparser.parse_from_string(raw_mail)
mail = mailparser.parse_from_bytes(byte_mail)

Cách sử dụng:

mail.attachments: list of all attachments
mail.body
mail.to

2
Thư viện thật tuyệt, nhưng tôi phải tạo lớp kế thừa từ MailParservà ghi đè phương thức body của riêng mình , vì nó kết hợp các phần của nội dung email với "\ n --- mail_boundary --- \ n" không lý tưởng đối với tôi.
avram 21/09/18

chào @avram, bạn có thể vui lòng chia sẻ lớp học mà bạn đã viết được không?
Amey P Naik

Tôi đã tách kết quả trên "\ n --- mail_boundary --- \ n".
Amey P Naik

1
@AmeyPNaik Ở đây tôi đã tạo nhanh ý chính trên github: gist.github.com/aleksaa01/ccd371869f3a3c7b3e47822d5d78ccdf
avram

1
@AmeyPNaik trong tài liệu của họ cho biết: trình phân tích cú pháp thư có thể phân tích cú pháp định dạng email Outlook (.msg). Để sử dụng tính năng này, bạn cần cài đặt gói libemail-outlook-message-perl
Ciprian Tomoiagă

6

Python 3.6+ cung cấp các phương thức tiện lợi được tích hợp sẵn để tìm và giải mã phần nội dung văn bản thuần túy như trong @Todor Minakovcâu trả lời của. Bạn có thể sử dụng EMailMessage.get_body()get_content()các phương pháp:

msg = email.message_from_string(s, policy=email.policy.default)
body = msg.get_body(('plain',))
if body:
    body = body.get_content()
print(body)

Lưu ý rằng điều này sẽ Nonecó hiệu lực nếu không có phần nội dung văn bản thuần túy (rõ ràng).

Nếu bạn đang đọc từ một tệp mbox, ví dụ, bạn có thể cung cấp cho hàm tạo hộp thư một EmailMessagenhà máy:

mbox = mailbox.mbox(mboxfile, factory=lambda f: email.message_from_binary_file(f, policy=email.policy.default), create=False)
for msg in mbox:
    ...

Lưu ý rằng bạn phải vượt qua email.policy.defaultnhư chính sách, vì nó không phải mặc định ...


2
Tại sao không phải email.policy.defaultlà mặc định? Có vẻ như nó nên được.
Đặt hàng một phần

4

Không có b['body']trong python. Bạn phải sử dụng get_payload.

if isinstance(mailEntity.get_payload(), list):
    for eachPayload in mailEntity.get_payload():
        ...do things you want...
        ...real mail body is in eachPayload.get_payload()...
else:
    ...means there is only text/plain part....
    ...use mailEntity.get_payload() to get the body...

Chúc may mắn.


0

Nếu email là khung dữ liệu gấu trúc và email. Đánh dấu cột cho văn bản email

## Helper functions
def get_text_from_email(msg):
    '''To get the content from email objects'''
    parts = []
    for part in msg.walk():
        if part.get_content_type() == 'text/plain':
            parts.append( part.get_payload() )
    return ''.join(parts)

def split_email_addresses(line):
    '''To separate multiple email addresses'''
    if line:
        addrs = line.split(',')
        addrs = frozenset(map(lambda x: x.strip(), addrs))
    else:
        addrs = None
    return addrs 

import email
# Parse the emails into a list email objects
messages = list(map(email.message_from_string, emails['message']))
emails.drop('message', axis=1, inplace=True)
# Get fields from parsed email objects
keys = messages[0].keys()
for key in keys:
    emails[key] = [doc[key] for doc in messages]
# Parse content from emails
emails['content'] = list(map(get_text_from_email, messages))
# Split multiple email addresses
emails['From'] = emails['From'].map(split_email_addresses)
emails['To'] = emails['To'].map(split_email_addresses)

# Extract the root of 'file' as 'user'
emails['user'] = emails['file'].map(lambda x:x.split('/')[0])
del messages

emails.head()

-3

Đây là mã hoạt động cho tôi mọi lúc (đối với email Outlook):

#to read Subjects and Body of email in a folder (or subfolder)

import win32com.client  
#import package

outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")  
#create object

#get to the desired folder (MyEmail@xyz.com is my root folder)

root_folder = 
outlook.Folders['MyEmail@xyz.com'].Folders['Inbox'].Folders['SubFolderName']

#('Inbox' and 'SubFolderName' are the subfolders)

messages = root_folder.Items

for message in messages:
if message.Unread == True:    # gets only 'Unread' emails
    subject_content = message.subject
# to store subject lines of mails

    body_content = message.body
# to store Body of mails

    print(subject_content)
    print(body_content)

    message.Unread = True         # mark the mail as 'Read'
    message = messages.GetNext()  #iterate over mails

4
Có lẽ phải giải thích rằng điều này là dành cho Outlook trên Windows, không phải dành cho email thực.
tripleee
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.