existe-t-il un module Python qui aide à décoder les différentes formes d'en-têtes de messagerie codées, principalement Subject, en simples - disons - chaînes UTF-8?
Voici des exemples d'en-têtes de sujet des fichiers courrier que j'ai:
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?=
texte - aiguille codée - texte
texte - chaîne codée
texte - chaîne codée - chaîne codée
Encodig pourrait également être quelque chose d'autre comme ISO 8859-15.
Mise à jour 1: j'ai oublié de mentionner, j'ai essayé 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 )
Cette sorties
DEBUG: root: le sujet est [('[201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Januar 2011', aucun)]
ce qui n'aide pas vraiment.
Mise à jour 2: Merci à Ingmar Hupp dans les commentaires.
le premier exemple décode en une liste de deux tupels:
print decode_header ("" "[201105161048] GewSt: =? UTF-8? B? IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0? =" "")
[('[201105161048] GewSt:', None), ('Wegfall der Vorl\xc3\xa4ufigkeit', 'utf-8')]
est-ce toujours [(chaîne, encodage), (chaîne, encodage), ...] donc j'ai besoin d'une boucle pour concaténer tous les éléments [0] en une seule chaîne ou comment obtenir le tout en une seule chaîne?
Objet: [201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Januar 2011
ne décode pas bien:
print decode_header ("" "[201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Januar 2011" "")
[('[201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Januar 2011', Aucun)]
Ce type d'encodage est connu sous le nom de MIME encoded-Word et le module email peut le décoder:
from email.header import decode_header
print decode_header("""=?UTF-8?B?IERyZWltb25hdHNmcmlzdCBmw7xyIFZlcnBmbGVndW5nc21laHJhdWZ3ZW5kdW4=?=""")
Cela génère une liste de tuples, contenant la chaîne décodée et l'encodage utilisé. En effet, le format prend en charge différents encodages dans un seul en-tête. Pour les fusionner en une seule chaîne, vous devez les convertir en un codage partagé, puis les concaténer, ce qui peut être accompli en utilisant l'objet 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 ])
Le problème avec cette ligne Objet ne décodant pas:
Subject: [ 201101251025 ] ELStAM;=?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=. Januar 2011
^
Est en fait la faute de l'expéditeur, qui viole l'exigence de mots codés dans un en-tête étant séparés par un espace blanc, spécifié dans RFC 2047, section 5, paragraphe 1 : un "mot codé" qui apparaît dans un champ d'en-tête défini comme "* texte" DOIT être séparé de tout "mot codé" ou "texte" adjacent par un "espace blanc linéaire".
Si nécessaire, vous pouvez contourner ce problème en prétraitant ces en-têtes corrompus avec une expression régulière qui insère un espace après la partie Word encodé (sauf si elle est à la fin), comme ceci:
import re
header_value = re.sub(r"(=\?.*\?=)(?!$)", r"\1 ", header_value)
Je testais juste avec des en-têtes codés dans Python 3.3, et j'ai trouvé que c'était un moyen très pratique de les gérer:
>>> 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'
Comme vous pouvez le voir, il ajoute automatiquement des espaces autour des mots encodés.
Il conserve en interne les parties d'en-tête codées et ASCII séparées comme vous pouvez le voir quand il recode les parties non ASCII:
>>> h.encode()
'[ 201105161048 ] GewSt: =?utf-8?q?_Wegfall_der_Vorl=C3=A4ufigkeit?='
Si vous souhaitez que l'en-tête entier soit recodé, vous pouvez convertir l'en-tête en chaîne, puis le recomposer en en-tête:
>>> 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?='
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)))
Que diriez-vous de décoder les en-têtes de la manière suivante:
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)
cela devient l'objet de l'e-mail et le décode avec l'encodage spécifié (ou pas de décodage si l'encodage est défini sur Aucun).
A travaillé pour moi pour les encodages définis comme "Aucun", "utf-8", "koi8-r", "cp1251", "windows-1251"
J'ai eu un problème similaire, mais mon cas était un peu différent:
Maintenant, la fonctionnalité intéressante du python 3 email.parser est que tous les en-têtes sont automatiquement décodés en Unicode-Strings. Cependant, cela provoque un peu de "malheur" quand il s'agit de mauvais en-têtes. le problème:
Subject: Re: =?ISO-2022-JP?B?GyRCIVYlMyUiMnE1RCFXGyhC?=
(1/9(=?ISO-2022-JP?B?GyRCNmIbKEI=?=) 6:00pm-7:00pm)
=?ISO-2022-JP?B?GyRCJE4kKkNOJGkkOxsoQg==?=
Cela a abouti à msg['subject']
:
Re: 「コア会議」 (1/9(=?ISO-2022-JP?B?GyRCNmIbKEI=?=) 6:00pm-7:00pm) のお知らせ
Eh bien, le problème est le non-respect de la RFC 2047 (il devrait y avoir un espace blanc en ligne après le mot codé MIME) comme déjà décrit dans le réponse d'Ingmar Hupp . Ma réponse est donc inspirée de la sienne.
Solution 1: Correction de la chaîne d'octets avant d'analyser réellement l'e-mail. Cela semblait être la meilleure solution, mais j'avais du mal à implémenter une substitution Regex sur les chaînes d'octets. J'ai donc opté pour la solution 2:
Solution 2: Correction de la valeur d'en-tête déjà analysée et partiellement décodée:
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
Explication des parties importantes:
J'ai modifié l'expression régulière d'Ingmar Hupp pour ne remplacer que les mauvais mots codés MIME: (=\?.*\?=)(?=\S)
Démo Debuggex . Parce que faire pour tous ralentirait fortement l'analyse syntaxique (analyse d'environ 150'000 mails).
Après avoir appliqué le decode_header
fonction à fixed_header
, nous avons les parties suivantes dans 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)]
Pour re-décoder les séquences échappées unicode, nous définissons default_charset = 'unicode-escape'
lors de la création de la nouvelle valeur d'en-tête.
Le correct_header_value
est maintenant:
Re: 「コア会議」 (1/9(金 ) 6:00pm-7:00pm) のお知らせ'
J'espère que cela fera gagner du temps à quelqu'un.
Addition: Le réponse de Sander Steffann ne m'a pas vraiment aidé, car je n'ai pas pu extraire la valeur brute du champ d'en-tête de la classe de message.
Ce script fonctionne bien pour moi .. J'utilise ce script pour décoder tous les sujets des e-mails
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
échantillons:
=?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?=
from email.header import decode_header
mail = email.message_from_bytes(data[0][1])
subject_list = decode_header(mail['Subject'])
sub_list = []
for subject in subject_list:
if subject[1]:
subject = (subject[0].decode(subject[1]))
Elif type(subject[0]) == bytes:
subject = subject[0].decode('utf-8')
else:
subject = subject[0]
sub_list.append(subject)
subject = ''.join(sub_list)
print('Subject:' + subject)
Python possède une bibliothèque de courrier électronique. http://docs.python.org/library/email.header.html
Jetez un œil à email.header.decode_header ()