29

我希望能够使用 Python 将 GMail 中的电子邮件从收件箱移动到另一个文件夹。我正在使用 imaplib,但不知道该怎么做。

4

8 回答 8

45

There is no explicit move command for IMAP. You will have to execute a COPY followed by a STORE (with suitable flag to indicate deletion) and finally expunge. The example given below worked for moving messages from one label to the other. You'll probably want to add more error checking though.

import imaplib, getpass, re
pattern_uid = re.compile(r'\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)
于 2010-08-20T07:04:45.293 回答
5

至于 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)
于 2011-09-16T05:22:12.673 回答
4

我想一个人有一个将要移动的电子邮件的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()
于 2010-09-03T13:19:05.163 回答
3

以前的解决方案都不适合我。我无法从所选文件夹中删除邮件,并且当标签是所选文件夹时无法删除该文件夹的标签。这就是最终为我工作的东西:

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
于 2013-01-07T04:03:55.937 回答
2

这是将多个从一个文件夹移动到另一个文件夹的解决方案。

    mail_server = 'imap.gamil.com'
    account_id = 'yourimap@gmail.com'
    password = 'testpasword'
    TLS_port = '993'
    # connection to imap  
    conn = imaplib.IMAP4_SSL(mail_server,TLS_port)
    try:
        (retcode, capabilities) = conn.login(account_id, password)
        # return HttpResponse("pass")
    except:
        # return HttpResponse("fail")
        messages.error(request, 'Request Failed! Unable to connect to Mailbox. Please try again.')
        return redirect('addIEmMailboxes')

    conn.select('"INBOX"') 
    (retcode, messagess) = conn.uid('search', None, "ALL")
    if retcode == 'OK':
        for num in messagess[0].split():
            typ, data = conn.uid('fetch', num,'(RFC822)')
            msg = email.message_from_bytes((data[0][1]))
            #MOVE MESSAGE TO ProcessedEmails FOLDER
            result = conn.uid('COPY', num, 'ProcessedEmails')
            if result[0] == 'OK':
                mov, data = conn.uid('STORE', num , '+FLAGS', '(\Deleted)')
                conn.expunge()
            
    conn.close()
    return redirect('addIEmMailboxes')
于 2020-07-25T17:45:52.863 回答
1

我知道这是一个非常古老的问题,但无论如何。Manoj Govindan提出的解决方案可能效果很好(我还没有测试过,但看起来很像。我遇到并且必须解决的问题是如何复制/移动多个电子邮件!!!

所以我想出了解决方案,也许将来其他人可能会遇到同样的问题。

步骤很简单,我连接到我的电子邮件 (GMAIL) 帐户选择要处理的文件夹(例如收件箱)获取所有 uid,而不是电子邮件列表编号。这是这里要注意的一个关键点。如果我们获取电子邮件列表数量,然后我们处理列表,我们最终会遇到问题。当我们移动电子邮件时,过程很简单(在目标文件夹中复制并从每个当前位置删除电子邮件)。如果您有一个电子邮件列表,例如收件箱中有 4 封电子邮件,并且我们处理列表中的第二封电子邮件,那么 3 号和 4 号是不同的,它们不是我们认为的电子邮件,这将导致进入错误,因为列表项编号 4 它将不存在,因为列表向下移动了一个位置,因为 2 位置为空。

所以解决这个问题的唯一可能方法是使用 UID。这是每封电子邮件的唯一编号。所以无论邮箱怎么改,这个号码都会和邮箱绑定。

因此,在下面的示例中,我在第一步获取 UID,检查文件夹是否为空,无需处理文件夹,否则迭代文件夹中找到的所有电子邮件。接下来获取每个电子邮件标头。标题将帮助我们获取主题并将电子邮件的主题与我们正在搜索的主题进行比较。如果主题匹配,则继续复制并删除电子邮件。然后你就完成了。就那么简单。

#!/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 的有用信息 — 带有 Gmail 的 imaplib IMAP 示例imaplib 文档

于 2016-01-10T01:59:21.137 回答
0

我的外部库:https ://github.com/ikvk/imap_tools

# MOVE all messages from INBOX to INBOX/folder2
from imap_tools import MailBox
with MailBox('imap.ya.ru').login('tst@ya.ru', 'pwd', 'INBOX') as mailbox:
    mailbox.move(mailbox.fetch('ALL'), 'INBOX/folder2')  # *implicit creation of uid list on fetch
于 2020-07-03T04:50:02.150 回答
0

使用 Python 3 的解决方案,将 Zoho 邮件从垃圾箱移至存档。(Zoho 不会存档已删除的邮件,因此如果您想永久保留它们,您需要从垃圾箱移动到存档文件夹。)

#!/usr/bin/env python3

import imaplib, sys

obj = imaplib.IMAP4_SSL('imap.zoho.com', 993)
obj.login('account', 'password')
obj.select('Trash')
_, data = obj.uid('FETCH', '1:*' , '(RFC822.HEADER)')

if data[0] is None:
    print("No messages in Trash")
    sys.exit(0)

messages = [data[i][0].split()[2]  for i in range(0, len(data), 2)]
for msg_uid in messages:
    apply_lbl_msg = obj.uid('COPY', msg_uid, 'Archive')
    if apply_lbl_msg[0] == 'OK':
        mov, data = obj.uid('STORE', msg_uid , '+FLAGS', '(\Deleted)')
        obj.expunge()
        print("Moved msg %s" % msg_uid)
    else:
        print("Copy of msg %s did not work" % msg_uid)
于 2020-06-28T22:10:17.160 回答