简体   繁体   English

使用 Python 和 imaplib 在 GMail 中移动 email

[英]Move an email in GMail with Python and imaplib

I want to be able to move an email in GMail from the inbox to another folder using Python.我希望能够使用 Python 将 GMail 中的 email 从收件箱移动到另一个文件夹。 I am using imaplib and can't figure out how to do it.我正在使用 imaplib,但不知道该怎么做。

There is no explicit move command for IMAP. IMAP 没有明确的移动命令。 You will have to execute a COPY followed by a STORE (with suitable flag to indicate deletion) and finally expunge .您必须先执行COPY然后执行STORE (带有适当的标志以指示删除),最后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)

As for Gmail, based on its api working with labels , the only thing for you to do is adding dest label and deleting src label:至于 Gmail,基于它的api 与 labels 一起工作,你唯一要做的就是添加 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)

I suppose one has a uid of the email which is going to be moved.我想一个人有一个将要移动的电子邮件的 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()

None of the previous solutions worked for me.以前的解决方案都不适合我。 I was unable to delete a message from the selected folder, and unable to remove the label for the folder when the label was the selected folder.我无法从所选文件夹中删除邮件,并且当标签是所选文件夹时无法删除文件夹的标签。 Here's what ended up working for me:以下是最终为我工作的内容:

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

This is the solution to move multiple from one folder to another.这是将多个文件夹从一个文件夹移动到另一个文件夹的解决方案。

    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')

I know that this is a very old question, but any way.我知道这是一个非常古老的问题,但无论如何。 The proposed solution by Manoj Govindan probably works perfectly (I have not tested it but it looks like it. The problem that I encounter and I had to solve is how to copy/move more than one email!!! Manoj Govindan提出的解决方案可能效果很好(我没有测试过,但看起来很像。我遇到并且必须解决的问题是如何复制/移动不止一封电子邮件!!!

So I came up with solution, maybe someone else in the future might have the same problem.所以我想出了解决方案,也许将来其他人可能会遇到同样的问题。

The steps are simple, I connect to my email (GMAIL) account choose folder to process (eg INBOX) fetch all uids, instead of email(s) list number.步骤很简单,我连接到我的电子邮件 (GMAIL) 帐户,选择要处理的文件夹(例如 INBOX)获取所有 uid,而不是电子邮件列表编号。 This is a crucial point to notice here.这是这里需要注意的关键点。 If we fetched the list number of emails and then we processed the list we would end up with a problem.如果我们获取电子邮件的列表编号,然后我们处理列表,我们最终会遇到问题。 When we move an email the process is simple (copy at the destination folder and delete email from each current location).当我们移动电子邮件时,过程很简单(在目标文件夹中复制并从每个当前位置删除电子邮件)。 The problem appears if you have a list of emails eg 4 emails inside the inbox and we process the 2nd email in inside the list then number 3 and 4 are different, they are not the emails that we thought that they would be, which will result into an error because list item number 4 it will not exist since the list moved one position down because 2 position was empty.如果您有一个电子邮件列表,例如收件箱中有 4 封电子邮件,并且我们在列表中处理第二封电子邮件,那么问题就出现了,然后数字 3 和 4 是不同的,它们不是我们认为它们会是的电子邮件,这将导致进入错误,因为列表项目编号 4 它将不存在,因为列表向下移动了一个位置,因为 2 个位置为空。

So the only possible solution to this problem was to use UIDs.所以这个问题唯一可能的解决方案是使用 UID。 Which are unique numbers for each email.这是每封电子邮件的唯一编号。 So no matter how the email will change this number will be binded with the email.所以无论电子邮件如何更改,这个号码都会与电子邮件绑定。

So in the example below, I fetch the UIDs on the first step,check if folder is empty no point of processing the folder else iterate for all emails found in the folder.因此,在下面的示例中,我在第一步中获取 UID,检查文件夹是否为空,无需处理文件夹,否则迭代文件夹中找到的所有电子邮件。 Next fetch each email Header.接下来获取每个电子邮件标题。 The headers will help us to fetch the Subject and compare the subject of the email with the one that we are searching.标题将帮助我们获取主题并将电子邮件的主题与我们正在搜索的主题进行比较。 If the subject matches, then continue to copy and delete the email.如果主题匹配,则继续复制并删除电子邮件。 Then you are done.然后你就完成了。 Simple as that.就那么简单。

#!/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)

Useful information regarding imaplib Python — imaplib IMAP example with Gmail and the imaplib documentation .有关 imaplib Python 的有用信息— imaplib IMAP 示例与 Gmailimaplib 文档

Solution with Python 3, to move Zoho mails from Trash to Archive.使用 Python 3 的解决方案,将 Zoho 邮件从垃圾箱移动到存档。 (Zoho does not archive deleted messages, so if you want to preserve them forever, you need to move from Trash to an archival folder.) (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)

My external lib: https://github.com/ikvk/imap_tools我的外部库: 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM