2012-04-20 44 views
10

Python novato aquí. Quiero recorrer un gran archivo mbox, analizar los mensajes de correo electrónico. Puedo hacer eso con:Analizando archivos mbox en Python

import sys 
import mailbox 

def gen_summary(filename): 
    mbox = mailbox.mbox(filename) 
    for message in mbox: 
     subj = message['subject'] 
     print subj 

if __name__ == "__main__": 
    if len(sys.argv) != 2: 
     print 'Usage: python genarchivesum.py mbox' 
     sys.exit(1) 

    gen_summary(sys.argv[1]) 

Pero necesito más control. Necesito poder obtener la posición de bytes del inicio de un correo electrónico dado en el archivo mbox y también necesito obtener el número de bytes en el mensaje (como se representa en el disco). Y luego, en el futuro, en lugar de iterar desde el principio del archivo mbox, necesito poder buscar un mensaje determinado y analizarlo (de ahí una de las necesidades de obtener la posición de byte en el disco). Estos son grandes archivos mbox y la eficiencia es una preocupación.

El propósito de todo esto es que puedo generar un archivo de resumen, que contiene algunos pequeños bits sobre cada correo electrónico en el mbox, y luego, en el futuro, buscar eficientemente los correos electrónicos individuales dentro del mbox.

+0

Nunca he usado 'mailbox', pero acabo de leer' help (mailbox.mbox) '. ¿No puedes usar el método '.ritkeys()' para obtener un iterador de valores clave, y luego usar los valores clave para encontrar mensajes? ¿Por qué quiere usar un índice de bytes como clave para encontrar un mensaje en lugar de usar el módulo ... ha intentado utilizar el módulo para indexar los mensajes por clave? Si lo has probado y es demasiado lento o algo así, dímelo. – steveha

+0

Digamos que tengo un mbox de 10,000 correos electrónicos. No quiero tener que leer/analizar/iterar más de 9.998 de ellos cuando solo quiero el último correo electrónico. Me gustaría buscar ese punto en el archivo mbox y simplemente leer ese mensaje. –

+0

@MarkFletcher ¿Lo entiendes? Si es así, por favor hágamelo saber. – kingmakerking

Respuesta

8

No he probado esto, pero algo como esto podría funcionar para usted. Simplemente abra el archivo (en modo binario para que sus recuentos de bytes sean correctos), y escanee a través de él, buscando mensajes.

def is_mail_start(line): 
    return line.startswith("From ") 

def build_index(fname): 
    with open(fname, "rb") as f: 
     i = 0 
     b = 0 
     # find start of first message 
     for line in f: 
      b += len(line) 
      if is_mail_start(line): 
       break 
     # find start of each message, and yield up (index, length) of previous message 
     for line in f: 
      if is_mail_start(line): 
       yield (i, b) 
       i += b 
       b = 0 
      b += len(line) 
     yield (i, b) # yield up (index, length) of last message 

# get index as a list 
mbox_index = list(build_index(fname)) 

Una vez que tenga el índice, puede utilizar el método .seek() en un objeto de archivo para buscar allí, y .read(length) en el objeto de archivo para leer un solo mensaje. Aunque no estoy seguro de cómo usará el módulo mailbox con una cadena; Creo que está destinado a trabajar en un buzón en el lugar. Tal vez haya algún otro módulo de análisis de correo electrónico que pueda usar.

+1

Ok, gracias. Supongo que usaré algo así como esta estrategia. Por cierto, el comienzo de un correo electrónico en un mbox comienza con 'De' (sin el :). Puedo usar email.Parser para analizar el correo electrónico. Gracias. –

+0

Editaré la respuesta para sacar el ':'. Yo * dije * que no lo probé ... ¡Buena suerte con su proyecto y que tenga un gran fin de semana! – steveha

+0

Por lo que vale, para los futuros usuarios, en realidad ambos, al menos en la última versión de OSX. def is_mail_start (línea): \t return line.startswith ("De") y no line.startswith ("De:") – adammenges