Récuperer des emails, problèmes d'encodages

Récuperer des emails, problèmes d'encodages - Python - Programmation

Marsh Posté le 09-12-2008 à 17:01:01    

Bonjour à tous,
 
Je cherche à réaliser un script afin de parser une partie de mes emails gmails afin d'en extraire certaines information (expéditeur, sujet, date et heure) afin de générer des statistiques.
J'ai à peu près réussi à me débrouiller tout seul jusqu'ici (c'est ma première utilisation de python) en fouillant la doc et le net grace à l'ami (?) google, mais je bloque maintenant.
 
J'arrive à extraire les données brutes et les parser pour les mettre dans un tableau excel en csv. Là où je bloque, c'est sur l'encodage des noms et des sujets. Un petit bout de code sera peut-être plus explicite :
 

Code :
  1. logging.info("Fetching emails %s -> %s (of %s)", min, max, nbmails)
  2.   r, data = mail.fetch(str(min) + ":" + str(max), "(BODY[HEADER.FIELDS (DATE FROM SUBJECT)])" )
  3.   __AssertOk(r)
  4.   logging.info("Parsing fetched emails" )
  5.   for mi in range(0, chunk*2, 2):
  6.     t = data[mi][1]
  7.     _date = email.utils.parsedate(re.search("Date: (.+)(\\r\\n)+", t).group(1))
  8.     mytime = time.mktime(_date)
  9.     _from = re.search("From: (.+)(\\r\\n)+", t).group(1)
  10.     logging.info(_from)
  11.     encoding = email.header.decode_header(_from)[0][1]
  12.     if encoding == None:
  13.       encoding = "ascii"
  14.     _name = rfc822.parseaddr(email.header.decode_header(_from)[0][0])[0].decode(encoding)
  15.     _email = rfc822.parseaddr(email.header.decode_header(_from)[0][0])[1]
  16.     subject = re.search("Subject: (.+)(\\r\\n)+", t).group(1)
  17.     encoding = email.header.decode_header(subject)[0][1]
  18.     if encoding == None:
  19.       encoding = "ascii"
  20.     subject = email.header.decode_header(subject)[0][0].decode(encoding)
  21.     out.writerow([str(_date[0]), str(_date[1]), str(_date[2]), str(datetime.date(_date[0], _date[1], _date[2]).weekday()), str(_date[3]), str(_date[4]), str(_date[5]), str(mytime), _name, _email, subject])


 
Là où je pèche, c'est que des fois ça décode, des fois ça décode pas ....
exemple :

"=?ISO-8859-1?Q?Micha=EBl_Jxxxt?=" <michael.jxxxt@gmail.com>

Non converti

=?ISO-8859-1?Q?"Micha=EBl_Jxxxt"_<michael.jxxxt@gmail.com>?=
Michaël Jxxxt

Converti

"=?ISO-8859-1?Q?Timoth=E9e_Cxxxl?=" <timothee.cxxxl@googlemail.com>

Non converti
 
Ensuite j'ai un autre soucis : l'écriture dans le csv ne semble pas prendre apprécier les différents encodage et je suis assez perdu :

Traceback (most recent call last):
  File "I:\mails\xxxx.py", line 200, in <module>
    out.writerow([str(_date[0]), str(_date[1]), str(_date[2]), str(datetime.date
(_date[0], _date[1], _date[2]).weekday()), str(_date[3]), str(_date[4]), str(_da
te[5]), str(mytime), _name, _email, subject])
UnicodeEncodeError: 'ascii' codec can't encode character u'\xff' in position 9:
ordinal not in range(128)


 
Quel est le format standard d'un fichier csv pour excel ? (j'ai pas trouvé dans l'aide de Excel).
 
Voila, merci d'avance pour votre aide, si je n'ai pas posé la question de la bonne manière et/ou si il y a besoin d'info demandez et j'essaierai d'être le plus précis possible. :jap:


Message édité par The_chosen_one le 09-12-2008 à 17:02:07
Reply

Marsh Posté le 09-12-2008 à 17:01:01   

Reply

Marsh Posté le 09-12-2008 à 20:44:41    

Bon alors j'ai trouvé :)
 
1. certains mails sont de la forme encode(nom + mail) et d'autres encode(nom) + mail :
j'ai séparé les 2 cas avec des tests
2. certains mails sont mal codés :
j'ai pas trouvé d'autre solution que de tester et faire les corrections spécifiquement  
 
Voila le code si ça interesse des gens :
 

Code :
  1. import rfc822
  2. import csv
  3. import codecs
  4. import imaplib
  5. import logging
  6. import getopt
  7. import sys
  8. import getpass
  9. import stringscanner
  10. import re
  11. import math
  12. import time
  13. import datetime
  14. import email
  15. import email.header
  16. import email.utils
  17. import cStringIO
  18. class UTF8Recoder:
  19.     """
  20.     Iterator that reads an encoded stream and reencodes the input to UTF-8
  21.     """
  22.     def __init__(self, f, encoding):
  23.         self.reader = codecs.getreader(encoding)(f)
  24.     def __iter__(self):
  25.         return self
  26.     def next(self):
  27.         return self.reader.next().encode("utf-8" )
  28. class UnicodeReader:
  29.     """
  30.     A CSV reader which will iterate over lines in the CSV file "f",
  31.     which is encoded in the given encoding.
  32.     """
  33.     def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
  34.         f = UTF8Recoder(f, encoding)
  35.         self.reader = csv.reader(f, dialect=dialect, **kwds)
  36.     def next(self):
  37.         row = self.reader.next()
  38.         return [unicode(s, "utf-8" ) for s in row]
  39.     def __iter__(self):
  40.         return self
  41. class UnicodeWriter:
  42.     """
  43.     A CSV writer which will write rows to CSV file "f",
  44.     which is encoded in the given encoding.
  45.     """
  46.     def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
  47.         # Redirect output to a queue
  48.         self.queue = cStringIO.StringIO()
  49.         self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
  50.         self.stream = f
  51.         self.encoder = codecs.getincrementalencoder(encoding)()
  52.     def writerow(self, row):
  53.         self.writer.writerow([s.encode("utf-8" ) for s in row])
  54.         # Fetch UTF-8 output from the queue ...
  55.         data = self.queue.getvalue()
  56.         data = data.decode("utf-8" )
  57.         # ... and reencode it into the target encoding
  58.         data = self.encoder.encode(data)
  59.         # write to the target stream
  60.         self.stream.write(data)
  61.         # empty queue
  62.         self.queue.truncate(0)
  63.     def writerows(self, rows):
  64.         for row in rows:
  65.             self.writerow(row)
  66. def GetOptsMap():
  67.   opts, args = getopt.getopt(sys.argv[1:], "", [
  68.       # Standard options
  69.       "username=", "password=", "to=",
  70.       # Other params
  71.       "chunk=", "server="])
  72.  
  73.   opts_map = {}
  74.   for name, value in opts:
  75.     opts_map[name[2:]] = value
  76.   assert "username" in opts_map
  77.  
  78.   if "password" not in opts_map:
  79.     opts_map["password"] = getpass.getpass(
  80.         prompt="Password for %s: " % opts_map["username"])
  81.  
  82.   assert "password" in opts_map
  83.   if "server" not in opts_map:
  84.     opts_map["server"] = "imap.gmail.com"
  85.   if "chunk" not in opts_map:
  86.     opts_map["chunk"] = 100
  87.   assert "server" in opts_map
  88.   assert "to" in opts_map
  89.  
  90.   return opts_map
  91. def GetMailboxes(mail):
  92.   logging.info("Getting mailboxes" )
  93.  
  94.   r, mailboxes_data = mail.list()
  95.   __AssertOk(r)
  96.  
  97.   mailboxes = []
  98.   for mailbox_data in mailboxes_data:
  99.     s = mailbox_data
  100.     mailboxes.append(s)
  101.  
  102.   return mailboxes
  103. def __ParseFetchReply(fetch_reply):
  104.   message_infos = []
  105.  
  106.   return message_infos
  107. def __AssertOk(response):
  108.   assert response == "OK"
  109. logging.basicConfig(level=logging.DEBUG, format="[%(asctime)s] %(message)s" )
  110. opts = GetOptsMap()
  111. imap_constructor = imaplib.IMAP4_SSL
  112. logging.info("Connecting" )
  113. mail = imap_constructor(opts["server"])
  114. logging.info("Logging in" )
  115. mail.login(opts["username"], opts["password"])
  116. mailbox = "[Gmail]/All Mail"
  117. logging.info("Selecting mailbox: \"%s\". If there is an error here, you must switch Gmail interface to English(US)." %mailbox)
  118. r,data=mail.select(mailbox)
  119. __AssertOk(r)
  120. searchstring = "(TO \""+opts["to"]+"\" )"
  121. logging.info("Searching mails for : %s" %searchstring)
  122. r,data=mail.search(None, searchstring)
  123. __AssertOk(r)
  124. all_mails = data[0].split(" " )
  125. nbmails = len(all_mails)
  126. logging.info("Got %s mails !", nbmails)
  127. out = UnicodeWriter(open('out.csv', 'w'), dialect=csv.excel, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL, lineterminator='\n')
  128. out.writerow(['year', 'month', 'day', 'dayofweek', 'hour', 'minute', 'sec', 'timestamp', 'name', 'addr', 'subject'])
  129. chunk = min(opts["chunk"], nbmails)
  130. for min in range(1, nbmails, chunk):
  131.   max = min + chunk
  132.   if max > nbmails:
  133.     max = nbmails
  134.     chunk = max - min
  135.    
  136.   logging.info("Fetching emails %s -> %s (of %s)", min, max, nbmails)
  137.   fetch_string = ",".join(all_mails[min:max])
  138.   r, data = mail.fetch(fetch_string, "(BODY[HEADER.FIELDS (DATE FROM SUBJECT)])" )
  139.   __AssertOk(r)
  140.   for mi in range(0, chunk*2, 2):
  141.     t = data[mi][1]
  142.     _date = email.utils.parsedate(re.search("Date: (.+)(\\r\\n)+", t).group(1))
  143.     mytime = time.mktime(_date)
  144.     _from = re.search("From: (.+)(\\r\\n)+", t).group(1)
  145.     _from=_from.replace("=?utf-8?Q?Micha=C3=ABl_Jxxxxt?= <michael.jxxxxt@gmail.com>", "\"=?utf-8?Q?Micha=C3=ABl_Jxxxxt?=\" <michael.jxxxxt@gmail.com>" )
  146.     encoding = email.header.decode_header(_from)[0][1]
  147.    
  148.     if encoding == None:
  149.       encoding = email.header.decode_header(rfc822.parseaddr(email.header.decode_header(_from)[0][0])[0])[0][1]
  150.      
  151.       if encoding == None:
  152.         encoding = "ascii"
  153.         _name = rfc822.parseaddr(email.header.decode_header(_from)[0][0])[0].decode(encoding)
  154.        
  155.       else:
  156.         _name= email.header.decode_header(rfc822.parseaddr(email.header.decode_header(_from)[0][0])[0])[0][0].decode(encoding)
  157.        
  158.     else:
  159.       _name = rfc822.parseaddr(email.header.decode_header(_from)[0][0])[0].decode(encoding)
  160.      
  161.     _email = rfc822.parseaddr(email.header.decode_header(_from)[0][0])[1]
  162.     subject = re.search("Subject: (.+)(\\r\\n)+", t).group(1)
  163.     encoding = email.header.decode_header(subject)[0][1]
  164.    
  165.     if encoding == None:
  166.       encoding = "ascii"
  167.      
  168.     subject = email.header.decode_header(subject)[0][0].decode(encoding)
  169.     while subject <> re.search("([rR][eE][_ ]?:[_ ]?)?(.+)", subject).group(2):
  170.       subject = re.search("([rR][eE][_ ]?:[_ ]?)?(.+)", subject).group(2)
  171.      
  172.     out.writerow([str(_date[0]), str(_date[1]), str(_date[2]), str(datetime.date(_date[0], _date[1], _date[2]).weekday()), str(_date[3]), str(_date[4]), str(_date[5]), str(mytime), _name, _email, subject])
  173. logging.info("Done" )


 
Du coup ne me reste qu'un problème mineur : j'enregistre le fichier en utf-8 alors que excel l'ouvre en ansi (et c'est moche), et je trouve pas l'encoding ansi en python ...

Reply

Marsh Posté le 09-12-2008 à 23:50:48    

J'ai vraiment pas le courage de me plonger dans un bazard pareil, mais je tiens juste à mentionner que <> est déprécié (et complètement retiré de Python 3), et que optparse est largement supérieur à getopt (plus pratique, plus flexible et plus puissant).


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed