انقل رسالة بريد إلكتروني في Gmail مع Python و Imaplib
سؤال
أريد أن أكون قادرًا على نقل بريد إلكتروني في Gmail من صندوق الوارد إلى مجلد آخر باستخدام Python. أنا أستخدم Imaplib ولا يمكنني معرفة كيفية القيام بذلك.
المحلول
لا يوجد أمر نقل صريح لـ IMAP. سيكون عليك تنفيذ أ COPY
متبوعة ب STORE
(مع علامة مناسبة للإشارة إلى الحذف) وأخيرا expunge
. كان المثال الوارد أدناه ينقل الرسائل من علامة إلى أخرى. ربما تريد إضافة المزيد من التحقق من الأخطاء.
import imaplib, getpass, re
pattern_uid = re.compile('\d+ \(UID (?P<uid>\d+)\)')
def connect(email):
imap = imaplib.IMAP4_SSL("imap.gmail.com")
password = getpass.getpass("Enter your password: ")
imap.login(email, password)
return imap
def disconnect(imap):
imap.logout()
def parse_uid(data):
match = pattern_uid.match(data)
return match.group('uid')
if __name__ == '__main__':
imap = connect('<your mail id>')
imap.select(mailbox = '<source folder>', readonly = False)
resp, items = imap.search(None, 'All')
email_ids = items[0].split()
latest_email_id = email_ids[-1] # Assuming that you are moving the latest email.
resp, data = imap.fetch(latest_email_id, "(UID)")
msg_uid = parse_uid(data[0])
result = imap.uid('COPY', msg_uid, '<destination folder>')
if result[0] == 'OK':
mov, data = imap.uid('STORE', msg_uid , '+FLAGS', '(\Deleted)')
imap.expunge()
disconnect(imap)
نصائح أخرى
أما بالنسبة لـ Gmail ، بناءً على ذلك API العمل مع العلامات, ، الشيء الوحيد الذي يجب عليك فعله هو إضافة تسمية مصير وحذف تسمية SRC:
import imaplib
obj = imaplib.IMAP4_SSL('imap.gmail.com', 993)
obj.login('username', 'password')
obj.select(src_folder_name)
typ, data = obj.uid('STORE', msg_uid, '+X-GM-LABELS', desti_folder_name)
typ, data = obj.uid('STORE', msg_uid, '-X-GM-LABELS', src_folder_name)
أفترض أن المرء لديه uid من البريد الإلكتروني الذي سيتم نقله.
import imaplib
obj = imaplib.IMAP4_SSL('imap.gmail.com', 993)
obj.login('username', 'password')
obj.select(src_folder_name)
apply_lbl_msg = obj.uid('COPY', msg_uid, desti_folder_name)
if apply_lbl_msg[0] == 'OK':
mov, data = obj.uid('STORE', msg_uid , '+FLAGS', '(\Deleted)')
obj.expunge()
لم يعمل أي من الحلول السابقة بالنسبة لي. لم أتمكن من حذف رسالة من المجلد المحدد ، ولم أتمكن من إزالة التسمية للمجلد عندما يكون التسمية المجلد المحدد. إليك ما انتهى به الأمر من أجل العمل بالنسبة لي:
import email, getpass, imaplib, os, sys, re
user = "user@example.com"
pwd = "password" #getpass.getpass("Enter your password: ")
m = imaplib.IMAP4_SSL("imap.gmail.com")
m.login(user,pwd)
from_folder = "Notes"
to_folder = "food"
m.select(from_folder, readonly = False)
response, emailids = imap.search(None, 'All')
assert response == 'OK'
emailids = emailids[0].split()
errors = []
labeled = []
for emailid in emailids:
result = m.fetch(emailid, '(X-GM-MSGID)')
if result[0] != 'OK':
errors.append(emailid)
continue
gm_msgid = re.findall(r"X-GM-MSGID (\d+)", result[1][0])[0]
result = m.store(emailid, '+X-GM-LABELS', to_folder)
if result[0] != 'OK':
errors.append(emailid)
continue
labeled.append(gm_msgid)
m.close()
m.select(to_folder, readonly = False)
errors2 = []
for gm_msgid in labeled:
result = m.search(None, '(X-GM-MSGID "%s")' % gm_msgid)
if result[0] != 'OK':
errors2.append(gm_msgid)
continue
emailid = result[1][0]
result = m.store(emailid, '-X-GM-LABELS', from_folder)
if result[0] != 'OK':
errors2.append(gm_msgid)
continue
m.close()
m.logout()
if errors: print >>sys.stderr, len(errors), "failed to add label", to_folder
if errors2: print >>sys.stderr, len(errors2), "failed to remove label", from_folder
أعلم أن هذا سؤال قديم للغاية ، ولكن بأي شكل من الأشكال. الحل المقترح بواسطة مانوج جوفيندان ربما يعمل بشكل مثالي (لم أختبره ولكن يبدو الأمر كذلك. المشكلة التي واجهتها واضطررت إلى حلها هي كيفية نسخ/نقل أكثر من بريد إلكتروني !!!
لذلك توصلت إلى حل ، ربما يكون لدى شخص آخر في المستقبل نفس المشكلة.
الخطوات بسيطة ، أقوم بالاتصال بحساب البريد الإلكتروني الخاص بي (Gmail) ، اختر المجلد لمعالجة (EG Box) جلب جميع UIDs ، بدلاً من رقم قائمة البريد الإلكتروني (البريد الإلكتروني). هذه نقطة حاسمة لإشعارها هنا. إذا جلبنا رقم قائمة رسائل البريد الإلكتروني ، ثم قمنا بمعالجة القائمة ، فسننتهي بمشكلة. عندما ننقل رسالة بريد إلكتروني ، تكون العملية بسيطة (انسخ في مجلد الوجهة وحذف البريد الإلكتروني من كل موقع حالي). تظهر المشكلة إذا كان لديك قائمة من رسائل البريد الإلكتروني على سبيل المثال 4 رسائل بريد إلكتروني داخل صندوق الوارد ونقوم بمعالجة البريد الإلكتروني الثاني في القائمة ثم الرقم 3 و 4 مختلفون ، فهي ليست رسائل البريد الإلكتروني التي اعتقدنا أنها ستكون ، والتي ستنتج في خطأ لأن عنصر القائمة رقم 4 لن يكون موجودًا لأن القائمة نقلت موقعًا واحدًا لأن وضعين كان فارغًا.
لذلك كان الحل الوحيد الممكن لهذه المشكلة هو استخدام UIDs. وهي أرقام فريدة لكل بريد إلكتروني. لذا ، بغض النظر عن كيفية تغيير البريد الإلكتروني ، سيتم إبطال هذا الرقم بالبريد الإلكتروني.
لذلك في المثال أدناه ، أحضر UIDs في الخطوة الأولى ، تحقق مما إذا كان المجلد فارغًا بدون نقطة لمعالجة المجلد الآخر الذي يكرر لجميع رسائل البريد الإلكتروني الموجودة في المجلد. قم بإحضار كل رأس بريد إلكتروني. ستساعدنا الرؤوس على جلب الموضوع ومقارنة موضوع البريد الإلكتروني مع البرنامج الذي نبحث عنه. إذا كان الموضوع يتطابق ، فاستمر في نسخ البريد الإلكتروني وحذفه. ثم انتهيت. سهل هكذا.
#!/usr/bin/env python
import email
import pprint
import imaplib
__author__ = 'author'
def initialization_process(user_name, user_password, folder):
imap4 = imaplib.IMAP4_SSL('imap.gmail.com') # Connects over an SSL encrypted socket
imap4.login(user_name, user_password)
imap4.list() # List of "folders" aka labels in gmail
imap4.select(folder) # Default INBOX folder alternative select('FOLDER')
return imap4
def logout_process(imap4):
imap4.close()
imap4.logout()
return
def main(user_email, user_pass, scan_folder, subject_match, destination_folder):
try:
imap4 = initialization_process(user_email, user_pass, scan_folder)
result, items = imap4.uid('search', None, "ALL") # search and return uids
dictionary = {}
if items == ['']:
dictionary[scan_folder] = 'Is Empty'
else:
for uid in items[0].split(): # Each uid is a space separated string
dictionary[uid] = {'MESSAGE BODY': None, 'BOOKING': None, 'SUBJECT': None, 'RESULT': None}
result, header = imap4.uid('fetch', uid, '(UID BODY[HEADER])')
if result != 'OK':
raise Exception('Can not retrieve "Header" from EMAIL: {}'.format(uid))
subject = email.message_from_string(header[0][1])
subject = subject['Subject']
if subject is None:
dictionary[uid]['SUBJECT'] = '(no subject)'
else:
dictionary[uid]['SUBJECT'] = subject
if subject_match in dictionary[uid]['SUBJECT']:
result, body = imap4.uid('fetch', uid, '(UID BODY[TEXT])')
if result != 'OK':
raise Exception('Can not retrieve "Body" from EMAIL: {}'.format(uid))
dictionary[uid]['MESSAGE BODY'] = body[0][1]
list_body = dictionary[uid]['MESSAGE BODY'].splitlines()
result, copy = imap4.uid('COPY', uid, destination_folder)
if result == 'OK':
dictionary[uid]['RESULT'] = 'COPIED'
result, delete = imap4.uid('STORE', uid, '+FLAGS', '(\Deleted)')
imap4.expunge()
if result == 'OK':
dictionary[uid]['RESULT'] = 'COPIED/DELETED'
elif result != 'OK':
dictionary[uid]['RESULT'] = 'ERROR'
continue
elif result != 'OK':
dictionary[uid]['RESULT'] = 'ERROR'
continue
else:
print "Do something with not matching emails"
# do something else instead of copy
dictionary = {scan_folder: dictionary}
except imaplib.IMAP4.error as e:
print("Error, {}".format(e))
except Exception as e:
print("Error, {}".format(e))
finally:
logout_process(imap4)
return dictionary
if __name__ == "__main__":
username = 'example.email@gmail.com'
password = 'examplePassword'
main_dictionary = main(username, password, 'INBOX', 'BOKNING', 'TMP_FOLDER')
pprint.pprint(main_dictionary)
exit(0)
معلومات مفيدة بخصوص Imaplib Python - مثال Imaplib IMAP مع Gmail و ال وثائق imaplib.