thru sent sending mimemultipart mail library from enviar electronico correo python email email-headers

sent - Python-descifrado de encabezado de correo electrónico UTF-8



sending email from python (8)

¿Qué hay de decodificar encabezados de la siguiente manera:

import poplib, email from email.header import decode_header, make_header ... subject, encoding = decode_header(message.get(''subject''))[0] if encoding==None: print "/n%s (%s)/n"%(subject, encoding) else: print "/n%s (%s)/n"%(subject.decode(encoding), encoding)

esto obtiene sujeto del correo electrónico y lo decodifica con codificación especificada (o sin decodificación si la codificación se establece en Ninguno).

Funcionó para mí para codificaciones establecidas como ''Ninguna'', ''utf-8'', ''koi8-r'', ''cp1251'', ''windows-1251''

¿Hay algún módulo Python que ayude a decodificar las diversas formas de encabezados de correo codificados, principalmente Subject, a simples cadenas de caracteres UTF-8?

Aquí hay ejemplos de cabeceras de tema de archivos de correo que tengo:

Subject: [ 201105311136 ]=?UTF-8?B?IMKnIDE2NSBBYnM=?=. 1 AO; Subject: [ 201105161048 ] GewSt:=?UTF-8?B?IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0?= Subject: [ 201105191633 ] =?UTF-8?B?IERyZWltb25hdHNmcmlzdCBmw7xyIFZlcnBmbGVndW5nc21laHJhdWZ3ZW5kdW4=?= =?UTF-8?B?Z2VuIGVpbmVzIFNlZW1hbm5z?=

texto - picadura codificada - texto

texto - cadena codificada

texto - cadena codificada - cadena codificada

Encodig también podría ser algo más como ISO 8859-15.

Actualización 1: Olvidé mencionar, probé email.header.decode_header

for item in message.items(): if item[0] == ''Subject'': sub = email.header.decode_header(item[1]) logging.debug( ''Subject is %s'' % sub )

Estas salidas

DEBUG: root: El sujeto es [(''[201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Januar 2011'', Ninguno)]

que realmente no ayuda.

Actualización 2: Gracias a Ingmar Hupp en los comentarios.

el primer ejemplo decodifica a una lista de dos tupeles:

print decode_header ("" "[201105161048] GewSt: =? UTF-8? B? IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0? =" "")
[(''[201105161048] GewSt:'', Ninguno), (''Wegfall der Vorl / xc3 / xa4ufigkeit'', ''utf-8'')]

¿Esto es siempre [(cadena, codificación), (cadena, codificación), ...] entonces necesito un ciclo para concaturar todos los [0] elementos a una cadena o cómo obtenerlo todo en una cadena?

Asunto: [201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Enero de 2011

no decodifica bien:

print decode_header ("" "[201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Januar 2011" "")

[(''[201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Januar 2011'', Ninguno)]


Esta secuencia de comandos funciona bien para mí .. Yo uso esta secuencia de comandos para decodificar todos los temas de correo electrónico

pat2=re.compile(r''(([^=]*)=/?([^/?]*)/?([BbQq])/?([^/?]*)/?=([^=]*))'',re.IGNORECASE) def decodev2(a): data=pat2.findall(a) line=[] if data: for g in data: (raw,extra1,encoding,method,string,extra)=g extra1=extra1.replace(''/r'','''').replace(''/n'','''').strip() if len(extra1)>0: line.append(extra1) if method.lower()==''q'': string=quopri.decodestring(string) string=string.replace("_"," ").strip() if method.lower()==''b'': string=base64.b64decode(string) line.append(string.decode(encoding,errors=''ignore'')) extra=extra.replace(''/r'','''').replace(''/n'','''').strip() if len(extra)>0: line.append(extra) return "".join(line) else: return a

muestras:

=?iso-8859-1?q?una-al-dia_=2806/04/2017=29_Google_soluciona_102_vulnerabi?= =?iso-8859-1?q?lidades_en_Android?= =?UTF-8?Q?Al=C3=A9grate?= : =?UTF-8?Q?=20La=20compra=20de=20tu=20vehi?= =?UTF-8?Q?culo=20en=20tan=20s=C3=B3lo=2024h?= =?UTF-8?Q?=2E=2E=2E=20=C2=A1Valoraci=C3=B3n=20=26?= =?UTF-8?Q?ago=20=C2=A0inmediato=21?=


Estaba probando con encabezados codificados en Python 3.3, y descubrí que esta es una forma muy conveniente de lidiar con ellos:

>>> from email.header import Header, decode_header, make_header >>> subject = ''[ 201105161048 ] GewSt:=?UTF-8?B?IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0?='' >>> h = make_header(decode_header(subject)) >>> str(h) ''[ 201105161048 ] GewSt: Wegfall der Vorläufigkeit''

Como puede ver, automáticamente agrega espacios en blanco alrededor de las palabras codificadas.

Guarda internamente las partes de encabezado codificadas y ASCII por separado, como puede ver cuando vuelve a codificar las partes que no son ASCII:

>>> h.encode() ''[ 201105161048 ] GewSt: =?utf-8?q?_Wegfall_der_Vorl=C3=A4ufigkeit?=''

Si desea volver a codificar todo el encabezado, puede convertir el encabezado en una cadena y luego volver a un encabezado:

>>> h2 = Header(str(h)) >>> str(h2) ''[ 201105161048 ] GewSt: Wegfall der Vorläufigkeit'' >>> h2.encode() ''=?utf-8?q?=5B_201105161048_=5D_GewSt=3A__Wegfall_der_Vorl=C3=A4ufigkeit?=''


Este tipo de codificación se conoce como MIME encoded-word y el módulo de email puede decodificarlo:

from email.header import decode_header print decode_header("""=?UTF-8?B?IERyZWltb25hdHNmcmlzdCBmw7xyIFZlcnBmbGVndW5nc21laHJhdWZ3ZW5kdW4=?=""")

Esto genera una lista de tuplas que contiene la cadena decodificada y la codificación utilizada. Esto se debe a que el formato admite diferentes codificaciones en un solo encabezado. Para fusionarlos en una sola cadena, debe convertirlos en una codificación compartida y luego concatenar esto, lo que se puede lograr utilizando el objeto Unicode de Python:

from email.header import decode_header dh = decode_header("""[ 201105161048 ] GewSt:=?UTF-8?B?IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0?=""") default_charset = ''ASCII'' print ''''.join([ unicode(t[0], t[1] or default_charset) for t in dh ])

Actualización 2:

El problema con esta línea de asunto no es la decodificación:

Subject: [ 201101251025 ] ELStAM;=?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=. Januar 2011 ^

En realidad, es la falla del remitente, que viola el requisito de palabras codificadas en un encabezado separado por espacio en blanco, especificado en RFC 2047, sección 5, párrafo 1 : una ''palabra codificada'' que aparece en un campo de encabezado definido como '' * el texto ''DEBE estar separado de cualquier'' palabra-codificada ''o'' texto ''adyacente por'' espacio-blanco-lineal ''.

Si es necesario, puede solucionar esto preprocesando estos encabezados corruptos con una expresión regular que inserta un espacio en blanco después de la parte de palabra codificada (a menos que esté al final), así:

import re header_value = re.sub(r"(=/?.*/?=)(?!$)", r"/1 ", header_value)



Tuve un problema similar, pero mi caso fue un poco diferente:

  • Python 3.5 (La pregunta es de 2011, pero sigue siendo muy alta en google)
  • Leer mensaje directamente del archivo como byte-string

Ahora la característica más interesante de python 3 email.parser es que todos los encabezados se decodifican automáticamente en Unicode-Strings. Sin embargo, esto causa un poco de "mala suerte" cuando se trata de encabezados incorrectos. Entonces el siguiente encabezado causó el problema:

Subject: Re: =?ISO-2022-JP?B?GyRCIVYlMyUiMnE1RCFXGyhC?= (1/9(=?ISO-2022-JP?B?GyRCNmIbKEI=?=) 6:00pm-7:00pm) =?ISO-2022-JP?B?GyRCJE4kKkNOJGkkOxsoQg==?=

Esto dio como resultado el siguiente msg[''subject''] :

Re: 「コア会議」 (1/9(=?ISO-2022-JP?B?GyRCNmIbKEI=?=) 6:00pm-7:00pm) のお知らせ

Bueno, el problema es la falta de conformidad con RFC 2047 (debe haber una línea de espacio en blanco después de la palabra codificada MIME) como ya se ha descrito en la respuesta de Ingmar Hupp . Entonces mi respuesta está inspirada por la suya.

Solución 1: arregle el byte-string antes de realmente analizar el correo electrónico. Esta parecía ser la mejor solución, sin embargo estaba luchando por implementar una sustitución Regex en byte-strings. Así que opté por la solución 2:

Solución 2: repare el valor del encabezado ya analizado y parcialmente decodificado:

with open(file, ''rb'') as fp: # read as byte-string msg = email.message_from_binary_file(fp, policy=policy.default) subject_fixed = fix_wrong_encoded_words_header(msg[''subject'']) def fix_wrong_encoded_words_header(header_value): fixed_header_value = re.sub(r"(=/?.*/?=)(?=/S)", r"/1 ", header_value) if fixed_header_value == header_value: # nothing needed to fix return header_value else: dh = decode_header(fixed_header_value) default_charset = ''unicode-escape'' correct_header_value = ''''.join([str(t[0], t[1] or default_charset) for t in dh]) return correct_header_value

Explicación de partes importantes:

Modifiqué la expresión regular de Ingmar Hupp para reemplazar solo palabras codificadas MIME incorrectas: (=/?.*/?=)(?=/S) Debuggex Demo . Porque hacer para todos ralentizaría enormemente el análisis sintáctico (analizando alrededor de 150 000 correos electrónicos).

Después de aplicar la función decode_header al fixed_header , tenemos las siguientes partes en dh :

dh == [(b''Re: //u300c//u30b3//u30a2//u4f1a//u8b70//u300d (1/9('', None), (b''/x1b$B6b/x1b(B'', ''iso-2022-jp''), (b'' ) 6:00pm-7:00pm) //u306e//u304a//u77e5//u3089//u305b'', None)]

Para volver a decodificar las secuencias de escape unicode, establecemos default_charset = ''unicode-escape'' cuando construimos el nuevo valor de encabezado.

El correct_header_value es ahora:

Re: 「コア会議」 (1/9(金 ) 6:00pm-7:00pm) のお知らせ''

Espero que esto salve a alguien en algún momento.

Además: la respuesta de Sander Steffann realmente no me ayudó, porque no pude sacar el valor en bruto del campo de cabecera de la clase de mensaje.


para mí esto funcionó perfecto (y siempre me da una cadena):

dmsgsubject, dmsgsubjectencoding = email.header.decode_header(msg[''Subject''])[0] msgsubject = dmsgsubject.decode(*([dmsgsubjectencoding] if dmsgsubjectencoding else [])) if isinstance(dmsgsubject, bytes) else dmsgsubject


def decode_header(value): return '' ''.join((item[0].decode(item[1] or ''utf-8'').encode(''utf-8'') for item in email.header.decode_header(value)))