2011-08-23 27 views
10

Sto provando a usare Python 3 per estrarre il corpo dei messaggi di posta elettronica da un file mbox di thunderbird. È un account IMAP.Estrarre il corpo di un'e-mail dal file mbox, decodificarlo in testo normale indipendentemente da Charset e Codifica trasferimento contenuto

Mi piacerebbe avere la parte di testo del corpo dell'e-mail disponibile per elaborare una stringa unicode. Dovrebbe sembrare che l'e-mail sia in Thunderbird e non contenere caratteri di escape come \ r \ n = 20 ecc.

Penso che siano le codifiche di trasferimento dei contenuti che non so come decodificare o rimuovere. Ricevo email con diversi tipi di contenuto e diverse codifiche di trasferimento del contenuto. Questo è il mio tentativo in corso:

import mailbox 
import quopri,base64 

def myconvert(encoded,ContentTransferEncoding): 
    if ContentTransferEncoding == 'quoted-printable': 
     result = quopri.decodestring(encoded) 
    elif ContentTransferEncoding == 'base64': 
     result = base64.b64decode(encoded) 

mboxfile = 'C:/Users/Username/Documents/Thunderbird/Data/profile/ImapMail/server.name/INBOX' 

for msg in mailbox.mbox(mboxfile): 
    if msg.is_multipart(): #Walk through the parts of the email to find the text body. 
     for part in msg.walk(): 
      if part.is_multipart(): # If part is multipart, walk through the subparts. 
       for subpart in part.walk(): 
        if subpart.get_content_type() == 'text/plain': 
         body = subpart.get_payload() # Get the subpart payload (i.e the message body) 
        for k,v in subpart.items(): 
          if k == 'Content-Transfer-Encoding': 
           cte = v    # Keep the Content Transfer Encoding 
      elif subpart.get_content_type() == 'text/plain': 
       body = part.get_payload()   # part isn't multipart Get the payload 
       for k,v in part.items(): 
        if k == 'Content-Transfer-Encoding': 
         cte = v      # Keep the Content Transfer Encoding 

print(body) 
print('Body is of type:',type(body)) 
body = myconvert(body,cte) 
print(body) 

Ma questo non funziona con:

Body is of type: <class 'str'> 
Traceback (most recent call last): 
File "C:/Users/David/Documents/Python/test2.py", line 31, in <module> 
    body = myconvert(body,cte) 
File "C:/Users/David/Documents/Python/test2.py", line 6, in myconvert 
    result = quopri.decodestring(encoded) 
File "C:\Python32\lib\quopri.py", line 164, in decodestring 
    return a2b_qp(s, header=header) 
TypeError: 'str' does not support the buffer interface 
+0

Questo è strano. get_payload() dovrebbe restituire byte, ma str con Python 3, a meno che tu non passi in 'decode = True', cosa che non fai. –

+0

L'ho appena provato con decode = True, che restituisce byte, quindi non c'è un errore. Sembra che la decodifica sia stata eseguita e ora tutto ciò che devo fare è convertire i byte in stringhe. Sebbene non l'abbia ancora provato con le e-mail con un'ampia varietà di codifica dei contenuti. – dcb

+0

Huh, sembra un bug, dovrebbe essere il contrario, decode = True dovrebbe restituire str e decode = False byte. :-) –

risposta

17

Ecco un codice che fa il lavoro, esso stampa gli errori invece di schiantarsi per quei messaggi in cui fallirebbe. Spero che possa essere utile. Nota che se c'è un bug in Python 3, e che è corretto, allora le righe .get_payload (decode = True) possono quindi restituire un oggetto str invece di un oggetto bytes. Ho eseguito questo codice oggi su 2.7.2 e su Python 3.2.1.

import mailbox 

def getcharsets(msg): 
    charsets = set({}) 
    for c in msg.get_charsets(): 
     if c is not None: 
      charsets.update([c]) 
    return charsets 

def handleerror(errmsg, emailmsg,cs): 
    print() 
    print(errmsg) 
    print("This error occurred while decoding with ",cs," charset.") 
    print("These charsets were found in the one email.",getcharsets(emailmsg)) 
    print("This is the subject:",emailmsg['subject']) 
    print("This is the sender:",emailmsg['From']) 

def getbodyfromemail(msg): 
    body = None 
    #Walk through the parts of the email to find the text body.  
    if msg.is_multipart():  
     for part in msg.walk(): 

      # If part is multipart, walk through the subparts.    
      if part.is_multipart(): 

       for subpart in part.walk(): 
        if subpart.get_content_type() == 'text/plain': 
         # Get the subpart payload (i.e the message body) 
         body = subpart.get_payload(decode=True) 
         #charset = subpart.get_charset() 

      # Part isn't multipart so get the email body 
      elif part.get_content_type() == 'text/plain': 
       body = part.get_payload(decode=True) 
       #charset = part.get_charset() 

    # If this isn't a multi-part message then get the payload (i.e the message body) 
    elif msg.get_content_type() == 'text/plain': 
     body = msg.get_payload(decode=True) 

    # No checking done to match the charset with the correct part. 
    for charset in getcharsets(msg): 
     try: 
      body = body.decode(charset) 
     except UnicodeDecodeError: 
      handleerror("UnicodeDecodeError: encountered.",msg,charset) 
     except AttributeError: 
      handleerror("AttributeError: encountered" ,msg,charset) 
    return body  


#mboxfile = 'C:/Users/Username/Documents/Thunderbird/Data/profile/ImapMail/server.name/INBOX' 
print(mboxfile) 
for thisemail in mailbox.mbox(mboxfile): 
    body = getbodyfromemail(thisemail) 
    print(body[0:1000]) 
2

Questo script sembra tornare in modo corretto tutti i messaggi:

def getcharsets(msg): 
    charsets = set({}) 
    for c in msg.get_charsets(): 
     if c is not None: 
      charsets.update([c]) 
    return charsets 

def getBody(msg): 
    while msg.is_multipart(): 
     msg=msg.get_payload()[0] 
    t=msg.get_payload(decode=True) 
    for charset in getcharsets(msg): 
     t=t.decode(charset) 
    return t 

Ex risposta da ACD spesso restituisce solo alcuni footer del messaggio vero e proprio. ( almeno nei Gmane messagens Email I am apertura per questa cassetta degli attrezzi: https://pypi.python.org/pypi/gmane )

applausi