Перемещение электронной почты в GMail с помощью Python и imaplib

Я хочу иметь возможность перемещать электронную почту в GMail из папки "Входящие" в другую папку с помощью Python. Я с помощью imaplib и не могу понять, как это сделать.

5 ответов


нет явной команды перемещения для 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 работает с метками, единственное, что вам нужно сделать, это добавить метку dest и удалить метку 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) выберите папку для обработки (например INBOX) извлеките все uids вместо номера списка электронной почты(ов). Это важный момент, который следует отметить здесь. Если бы мы получили список количество писем, а затем мы обработали список, мы бы в конечном итоге с проблемой. При перемещении электронной почты процесс прост (копирование в папку назначения и удаление электронной почты из каждого текущего местоположения). Проблема появляется, если у вас есть список писем, например, 4 письма внутри папки "Входящие", и мы обрабатываем 2-е письмо внутри списка, тогда номера 3 и 4 отличаются, они не письма, которые мы думали, что они будут, что приведет к ошибке, потому что пункт списка 4 он не будет существовать, так как список переместился на одну позицию вниз, потому что 2 позиция была пустой.

таким образом, единственным возможным решением этой проблемы было использование 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 документация.