簡體   English   中英

使用 Python 和 imaplib 在 GMail 中移動 email

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

我希望能夠使用 Python 將 GMail 中的 email 從收件箱移動到另一個文件夾。 我正在使用 imaplib,但不知道該怎么做。

IMAP 沒有明確的移動命令。 您必須先執行COPY然后執行STORE (帶有適當的標志以指示刪除),最后expunge 下面給出的示例用於將消息從一個標簽移動到另一個標簽。 不過,您可能希望添加更多錯誤檢查。

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)

至於 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)

我想一個人有一個將要移動的電子郵件的 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

這是將多個文件夾從一個文件夾移動到另一個文件夾的解決方案。

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

我知道這是一個非常古老的問題,但無論如何。 Manoj Govindan提出的解決方案可能效果很好(我沒有測試過,但看起來很像。我遇到並且必須解決的問題是如何復制/移動不止一封電子郵件!!!

所以我想出了解決方案,也許將來其他人可能會遇到同樣的問題。

步驟很簡單,我連接到我的電子郵件 (GMAIL) 帳戶,選擇要處理的文件夾(例如 INBOX)獲取所有 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 的有用信息— imaplib IMAP 示例與 Gmailimaplib 文檔

使用 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)

我的外部庫: 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