Κατακτήστε την αυτοματοποίηση email με το imaplib της Python. Αυτός ο οδηγός καλύπτει τη σύνδεση με διακομιστές IMAP, την αναζήτηση, ανάκτηση, ανάλυση emails, διαχείριση συνημμένων και θυρίδων.
Πελάτης IMAP σε Python: Ένας Ολοκληρωμένος Οδηγός για Ανάκτηση Email και Διαχείριση Θυρίδας
Το email παραμένει ακρογωνιαίος λίθος της ψηφιακής επικοινωνίας για επιχειρήσεις και ιδιώτες παγκοσμίως. Ωστόσο, η διαχείριση μεγάλου όγκου email μπορεί να είναι μια χρονοβόρα και επαναλαμβανόμενη εργασία. Από την επεξεργασία τιμολογίων και το φιλτράρισμα ειδοποιήσεων μέχρι την αρχειοθέτηση σημαντικών συνομιλιών, η χειροκίνητη προσπάθεια μπορεί γρήγορα να γίνει συντριπτική. Εδώ ακριβώς λάμπει ο προγραμματιστικός αυτοματισμός, και η Python, με την πλούσια τυπική της βιβλιοθήκη, παρέχει ισχυρά εργαλεία για να πάρετε τον έλεγχο του εισερχόμενου σας.
Αυτός ο ολοκληρωμένος οδηγός θα σας καθοδηγήσει στη διαδικασία δημιουργίας ενός πελάτη IMAP σε Python από το μηδέν χρησιμοποιώντας την ενσωματωμένη βιβλιοθήκη imaplib
. Θα μάθετε όχι μόνο πώς να ανακτάτε email, αλλά και πώς να αναλύετε το περιεχόμενό τους, να κατεβάζετε συνημμένα και να διαχειρίζεστε τη θυρίδα σας επισημαίνοντας μηνύματα ως αναγνωσμένα, μετακινώντας τα ή διαγράφοντάς τα. Μέχρι το τέλος αυτού του άρθρου, θα είστε εξοπλισμένοι για να αυτοματοποιήσετε τις πιο κουραστικές εργασίες email σας, εξοικονομώντας χρόνο και αυξάνοντας την παραγωγικότητά σας.
Κατανόηση των Πρωτοκόλλων: IMAP έναντι POP3 έναντι SMTP
Πριν βουτήξετε στον κώδικα, είναι απαραίτητο να κατανοήσετε τα θεμελιώδη πρωτόκολλα που διέπουν το email. Θα ακούτε συχνά τρία ακρωνύμια: SMTP, POP3 και IMAP. Το καθένα εξυπηρετεί έναν ξεχωριστό σκοπό.
- SMTP (Simple Mail Transfer Protocol): Αυτό είναι το πρωτόκολλο για την αποστολή email. Σκεφτείτε το SMTP ως την ταχυδρομική υπηρεσία που παραλαμβάνει το γράμμα σας και το παραδίδει στον διακομιστή θυρίδας του παραλήπτη. Όταν το σενάριο Python σας στέλνει ένα email, χρησιμοποιεί SMTP.
- POP3 (Post Office Protocol 3): Αυτό είναι ένα πρωτόκολλο για την ανάκτηση email. Το POP3 έχει σχεδιαστεί για να συνδέεται σε έναν διακομιστή, να κατεβάζει όλα τα νέα μηνύματα στον τοπικό σας πελάτη και στη συνέχεια, εξ ορισμού, να τα διαγράφει από τον διακομιστή. Είναι σαν να πηγαίνετε στο ταχυδρομείο, να συλλέγετε όλη την αλληλογραφία σας και να την παίρνετε σπίτι· μόλις είναι στο σπίτι σας, δεν είναι πλέον στο ταχυδρομείο. Αυτό το μοντέλο είναι λιγότερο κοινό σήμερα λόγω των περιορισμών του σε έναν κόσμο πολλαπλών συσκευών.
- IMAP (Internet Message Access Protocol): Αυτό είναι το σύγχρονο πρωτόκολλο για την πρόσβαση και διαχείριση email. Σε αντίθεση με το POP3, το IMAP αφήνει τα μηνύματα στον διακομιστή και συγχρονίζει την κατάσταση (αναγνωσμένο, μη αναγνωσμένο, με σημαία, διαγραμμένο) σε όλους τους συνδεδεμένους πελάτες. Όταν διαβάζετε ένα email στο τηλέφωνό σας, εμφανίζεται ως αναγνωσμένο στον φορητό σας υπολογιστή. Αυτό το διακομιστοκεντρικό μοντέλο είναι ιδανικό για αυτοματοποίηση, επειδή το σενάριο σας μπορεί να αλληλεπιδρά με τη θυρίδα ως άλλος πελάτης, και οι αλλαγές που κάνει θα αντικατοπτρίζονται παντού. Για αυτόν τον οδηγό, θα επικεντρωθούμε αποκλειστικά στο IMAP.
Ξεκινώντας με το imaplib
της Python
Η τυπική βιβλιοθήκη της Python περιλαμβάνει το imaplib
, μια ενότητα που παρέχει όλα τα απαραίτητα εργαλεία για επικοινωνία με έναν διακομιστή IMAP. Δεν απαιτούνται εξωτερικά πακέτα για να ξεκινήσετε.
Προαπαιτούμενα
- Εγκατεστημένη Python: Βεβαιωθείτε ότι έχετε εγκατεστημένη μια πρόσφατη έκδοση της Python (3.6 ή νεότερη) στο σύστημά σας.
- Ένας Λογαριασμός Email με ενεργοποιημένο IMAP: Οι περισσότεροι σύγχρονοι πάροχοι email (Gmail, Outlook, Yahoo, κ.λπ.) υποστηρίζουν IMAP. Ίσως χρειαστεί να το ενεργοποιήσετε στις ρυθμίσεις του λογαριασμού σας.
Πρώτα η Ασφάλεια: Χρησιμοποιήστε Κωδικούς Πρόσβασης Εφαρμογής, Όχι τον Κύριο Κωδικό σας
Αυτό είναι το πιο κρίσιμο βήμα για την ασφάλεια. Μην κωδικοποιείτε σκληρά τον κύριο κωδικό πρόσβασης του λογαριασμού email σας στο σενάριο σας. Αν ο κώδικάς σας παραβιαστεί ποτέ, ολόκληρος ο λογαριασμός σας κινδυνεύει. Οι περισσότεροι μεγάλοι πάροχοι email που χρησιμοποιούν Έλεγχο Ταυτότητας Δύο Παραγόντων (2FA) απαιτούν να δημιουργήσετε έναν "Κωδικό Πρόσβασης Εφαρμογής" (App Password).
Ένας Κωδικός Πρόσβασης Εφαρμογής είναι ένας μοναδικός, 16ψήφιος κωδικός που δίνει σε μια συγκεκριμένη εφαρμογή άδεια πρόσβασης στον λογαριασμό σας χωρίς να χρειάζεται τον κύριο κωδικό πρόσβασης ή τους κωδικούς 2FA. Μπορείτε να τον δημιουργήσετε και να τον ανακαλέσετε ανά πάσα στιγμή χωρίς να επηρεάσετε τον κύριο κωδικό πρόσβασης σας.
- Για Gmail: Μεταβείτε στις ρυθμίσεις του Λογαριασμού Google σας -> Ασφάλεια -> Επαλήθευση σε 2 βήματα -> Κωδικοί πρόσβασης εφαρμογών.
- Για Outlook/Microsoft: Μεταβείτε στον πίνακα ελέγχου ασφαλείας του Λογαριασμού Microsoft σας -> Επιλογές σύνθετης ασφάλειας -> Κωδικοί πρόσβασης εφαρμογών.
- Για άλλους παρόχους: Αναζητήστε στην τεκμηρίωσή τους για "κωδικός πρόσβασης εφαρμογής" ή "κωδικός πρόσβασης ειδικός για εφαρμογές".
Μόλις δημιουργηθεί, αντιμετωπίστε αυτόν τον Κωδικό Πρόσβασης Εφαρμογής όπως κάθε άλλο διαπιστευτήριο. Μια βέλτιστη πρακτική είναι να τον αποθηκεύσετε σε μια μεταβλητή περιβάλλοντος ή σε ένα ασφαλές σύστημα διαχείρισης μυστικών αντί απευθείας στον πηγαίο κώδικά σας.
Η Βασική Σύνδεση
Ας γράψουμε το πρώτο μας κομμάτι κώδικα για να δημιουργήσουμε μια ασφαλή σύνδεση με έναν διακομιστή IMAP, να συνδεθούμε και στη συνέχεια να αποσυνδεθούμε ομαλά. Θα χρησιμοποιήσουμε το imaplib.IMAP4_SSL
για να διασφαλίσουμε ότι η σύνδεσή μας είναι κρυπτογραφημένη.
import imaplib
import os
# --- Credentials ---
# It's best to load these from environment variables or a config file
# For this example, we'll define them here. Replace with your details.
EMAIL_ACCOUNT = "your_email@example.com"
APP_PASSWORD = "your_16_digit_app_password"
IMAP_SERVER = "imap.example.com" # e.g., "imap.gmail.com"
# --- Connect to the IMAP server ---
# We use a try...finally block to ensure we logout gracefully
conn = None
try:
# Connect using SSL for a secure connection
conn = imaplib.IMAP4_SSL(IMAP_SERVER)
# Login to the account
status, messages = conn.login(EMAIL_ACCOUNT, APP_PASSWORD)
if status == 'OK':
print("Successfully logged in!")
# We will add more logic here later
else:
print(f"Login failed: {messages}")
finally:
if conn:
# Always logout and close the connection
conn.logout()
print("Logged out and connection closed.")
Αυτό το σενάριο δημιουργεί μια βάση. Το μπλοκ try...finally
είναι ζωτικής σημασίας επειδή εγγυάται ότι καλείται η conn.logout()
, κλείνοντας τη συνεδρία με τον διακομιστή, ακόμα κι αν προκύψει σφάλμα κατά τη διάρκεια των λειτουργιών μας.
Πλοήγηση στη Θυρίδα σας
Μόλις συνδεθείτε, μπορείτε να αρχίσετε να αλληλεπιδράτε με τις θυρίδες (συχνά αποκαλούνται φάκελοι) στον λογαριασμό σας.
Λίστα Όλων των Θυρίδων
Για να δείτε ποιες θυρίδες είναι διαθέσιμες, μπορείτε να χρησιμοποιήσετε τη μέθοδο conn.list()
. Η έξοδος μπορεί να είναι λίγο ακατάστατη, οπότε απαιτείται λίγη ανάλυση για να πάρετε μια καθαρή λίστα ονομάτων.
# Inside the 'try' block after a successful login:
status, mailbox_list = conn.list()
if status == 'OK':
print("Available Mailboxes:")
for mailbox in mailbox_list:
# The raw mailbox entry is a byte string that needs decoding
# It's often formatted like: (\HasNoChildren) "/" "INBOX"
# We can do some basic parsing to clean it up
parts = mailbox.decode().split(' "/" ')
if len(parts) == 2:
mailbox_name = parts[1].strip('"')
print(f"- {mailbox_name}")
Αυτό θα εκτυπώσει μια λίστα όπως 'INBOX', 'Sent', '[Gmail]/Spam', κ.λπ., ανάλογα με τον πάροχο email σας.
Επιλογή Θυρίδας
Πριν αναζητήσετε ή ανακτήσετε email, πρέπει να επιλέξετε μια θυρίδα για να εργαστείτε. Η πιο κοινή επιλογή είναι το 'INBOX'. Η μέθοδος conn.select()
καθιστά μια θυρίδα ενεργή. Μπορείτε επίσης να την ανοίξετε σε λειτουργία μόνο για ανάγνωση εάν δεν σκοπεύετε να κάνετε αλλαγές (όπως να επισημάνετε email ως αναγνωσμένα).
# Select the 'INBOX' to work with.
# Use readonly=True if you don't want to change email flags (e.g., from UNSEEN to SEEN)
status, messages = conn.select('INBOX', readonly=False)
if status == 'OK':
total_messages = int(messages[0])
print(f"INBOX selected. Total messages: {total_messages}")
else:
print(f"Failed to select INBOX: {messages}")
Όταν επιλέγετε μια θυρίδα, ο διακομιστής επιστρέφει τον συνολικό αριθμό των μηνυμάτων που περιέχει. Όλες οι επόμενες εντολές για αναζήτηση και ανάκτηση θα εφαρμόζονται σε αυτήν την επιλεγμένη θυρίδα.
Αναζήτηση και Ανάκτηση Emails
Αυτός είναι ο πυρήνας της ανάκτησης email. Η διαδικασία περιλαμβάνει δύο βήματα: πρώτον, την αναζήτηση μηνυμάτων που ταιριάζουν σε συγκεκριμένα κριτήρια για να λάβετε τα μοναδικά τους αναγνωριστικά (IDs), και δεύτερον, την ανάκτηση του περιεχομένου αυτών των μηνυμάτων χρησιμοποιώντας τα IDs τους.
Η Δύναμη του `search()`
Η μέθοδος search()
είναι απίστευτα ευέλικτη. Δεν επιστρέφει τα ίδια τα email, αλλά μια λίστα αριθμών ακολουθίας μηνυμάτων (IDs) που ταιριάζουν με το ερώτημά σας. Αυτά τα IDs είναι συγκεκριμένα για την τρέχουσα συνεδρία και την επιλεγμένη θυρίδα.
Εδώ είναι μερικά από τα πιο κοινά κριτήρια αναζήτησης:
'ALL'
: Όλα τα μηνύματα στη θυρίδα.'UNSEEN'
: Μηνύματα που δεν έχουν διαβαστεί ακόμα.'SEEN'
: Μηνύματα που έχουν διαβαστεί.'FROM "sender@example.com"'
: Μηνύματα από συγκεκριμένο αποστολέα.'TO "recipient@example.com"'
: Μηνύματα που στάλθηκαν σε συγκεκριμένο παραλήπτη.'SUBJECT "Your Subject Line"'
: Μηνύματα με συγκεκριμένο θέμα.'BODY "a keyword in the body"'
: Μηνύματα που περιέχουν μια συγκεκριμένη συμβολοσειρά στο σώμα.'SINCE "01-Jan-2024"'
: Μηνύματα που ελήφθησαν στις ή μετά από μια συγκεκριμένη ημερομηνία.'BEFORE "31-Jan-2024"'
: Μηνύματα που ελήφθησαν πριν από μια συγκεκριμένη ημερομηνία.
Μπορείτε επίσης να συνδυάσετε κριτήρια. Για παράδειγμα, για να βρείτε όλα τα μη αναγνωσμένα email από έναν συγκεκριμένο αποστολέα με ένα συγκεκριμένο θέμα, θα αναζητούσατε το '(UNSEEN FROM "alerts@example.com" SUBJECT "System Alert")'
.
Ας το δούμε σε δράση:
# Search for all unread emails in the INBOX
status, message_ids = conn.search(None, 'UNSEEN')
if status == 'OK':
# message_ids is a list of byte strings, e.g., [b'1 2 3']
# We need to split it into individual IDs
email_id_list = message_ids[0].split()
if email_id_list:
print(f"Found {len(email_id_list)} unread emails.")
else:
print("No unread emails found.")
else:
print("Search failed.")
Ανάκτηση Περιεχομένου Email με το `fetch()`
Τώρα που έχετε τα αναγνωριστικά μηνυμάτων, μπορείτε να χρησιμοποιήσετε τη μέθοδο fetch()
για να ανακτήσετε τα πραγματικά δεδομένα του email. Πρέπει να καθορίσετε ποια μέρη του email θέλετε.
'RFC822'
: Αυτό ανακτά ολόκληρο το ακατέργαστο περιεχόμενο του email, συμπεριλαμβανομένων όλων των κεφαλίδων και των μερών του σώματος. Είναι η πιο κοινή και περιεκτική επιλογή.'BODY[]'
: Συνώνυμο τουRFC822
.'ENVELOPE'
: Ανακτά βασικές πληροφορίες κεφαλίδας όπως Ημερομηνία, Θέμα, Από, Προς και In-Reply-To. Αυτό είναι πιο γρήγορο αν χρειάζεστε μόνο μεταδεδομένα.'BODY[HEADER]'
: Ανακτά μόνο τις κεφαλίδες.
Ας ανακτήσουμε το πλήρες περιεχόμενο του πρώτου μη αναγνωσμένου email που βρήκαμε:
if email_id_list:
first_email_id = email_id_list[0]
# Fetch the email data for the given ID
# 'RFC822' is a standard that specifies the format of text messages
status, msg_data = conn.fetch(first_email_id, '(RFC822)')
if status == 'OK':
for response_part in msg_data:
# The fetch command returns a tuple, where the second part is the email content
if isinstance(response_part, tuple):
raw_email = response_part[1]
# Now we have the raw email data as bytes
# The next step is to parse it
print("Successfully fetched an email.")
# We will process `raw_email` in the next section
else:
print("Fetch failed.")
Ανάλυση Περιεχομένου Email με την ενότητα `email`
Τα ακατέργαστα δεδομένα που επιστρέφονται από το fetch()
είναι μια συμβολοσειρά byte διαμορφωμένη σύμφωνα με το πρότυπο RFC 822. Δεν είναι εύκολα αναγνώσιμη. Η ενσωματωμένη ενότητα email
της Python έχει σχεδιαστεί ειδικά για να αναλύει αυτά τα ακατέργαστα μηνύματα σε μια φιλική προς τον χρήστη δομή αντικειμένων.
Δημιουργία Αντικειμένου `Message`
Το πρώτο βήμα είναι η μετατροπή της ακατέργαστης συμβολοσειράς byte σε ένα αντικείμενο Message
χρησιμοποιώντας το email.message_from_bytes()
.
import email
from email.header import decode_header
# Assuming `raw_email` contains the byte data from the fetch command
email_message = email.message_from_bytes(raw_email)
Εξαγωγή Βασικών Πληροφοριών (Κεφαλίδες)
Μόλις έχετε το αντικείμενο Message
, μπορείτε να έχετε πρόσβαση στις κεφαλίδες του όπως ένα λεξικό.
# Get subject, from, to, and date
subject = email_message["Subject"]
from_ = email_message["From"]
to_ = email_message["To"]
date_ = email_message["Date"]
# Email headers can contain non-ASCII characters, so we need to decode them
def decode_email_header(header):
decoded_parts = decode_header(header)
header_str = ""
for part, encoding in decoded_parts:
if isinstance(part, bytes):
# If there's an encoding, use it. Otherwise, default to utf-8.
header_str += part.decode(encoding or 'utf-8')
else:
header_str += part
return header_str
subject = decode_email_header(subject)
from_ = decode_email_header(from_)
print(f"Subject: {subject}")
print(f"From: {from_}")
Η βοηθητική συνάρτηση decode_email_header
είναι σημαντική επειδή οι κεφαλίδες συχνά κωδικοποιούνται για να χειριστούν διεθνή σύνολα χαρακτήρων. Η απλή πρόσβαση στο email_message["Subject"]
μπορεί να σας δώσει μια συμβολοσειρά με μπερδεμένες ακολουθίες χαρακτήρων αν δεν την αποκωδικοποιήσετε σωστά.
Χειρισμός Σωμάτων Email και Συνημμένων
Τα σύγχρονα email είναι συχνά "πολυμερή", πράγμα που σημαίνει ότι περιέχουν διαφορετικές εκδόσεις του περιεχομένου (όπως απλό κείμενο και HTML) και ενδέχεται να περιλαμβάνουν συνημμένα. Πρέπει να περάσουμε από αυτά τα μέρη για να βρούμε αυτό που ψάχνουμε.
Η μέθοδος msg.is_multipart()
μας λέει αν ένα email έχει πολλά μέρη, και η msg.walk()
παρέχει έναν εύκολο τρόπο επανάληψης μέσω αυτών.
def process_email_body(msg):
body = ""
attachments = []
if msg.is_multipart():
# Iterate through email parts
for part in msg.walk():
content_type = part.get_content_type()
content_disposition = str(part.get("Content-Disposition"))
try:
# Get the email body
if content_type == "text/plain" and "attachment" not in content_disposition:
payload = part.get_payload(decode=True)
charset = part.get_content_charset() or 'utf-8'
body = payload.decode(charset)
# Get attachments
elif "attachment" in content_disposition:
filename = part.get_filename()
if filename:
# Decode filename if needed
decoded_filename = decode_email_header(filename)
attachments.append({
'filename': decoded_filename,
'data': part.get_payload(decode=True)
})
except Exception as e:
print(f"Error processing part: {e}")
else:
# Not a multipart message, just get the payload
payload = msg.get_payload(decode=True)
charset = msg.get_content_charset() or 'utf-8'
body = payload.decode(charset)
return body, attachments
# Using the function with our fetched message
email_body, email_attachments = process_email_body(email_message)
print("\n--- Email Body ---")
print(email_body)
if email_attachments:
print("\n--- Attachments ---")
for att in email_attachments:
print(f"Filename: {att['filename']}")
# Example of saving an attachment
with open(att['filename'], 'wb') as f:
f.write(att['data'])
print(f"Saved attachment: {att['filename']}")
Αυτή η συνάρτηση διαχωρίζει έξυπνα μεταξύ του σώματος απλού κειμένου και των συνημμένων αρχείων, εξετάζοντας τις κεφαλίδες Content-Type
και Content-Disposition
κάθε μέρους.
Προηγμένη Διαχείριση Θυρίδας
Η ανάκτηση email είναι μόνο η μισή μάχη. Ο πραγματικός αυτοματισμός περιλαμβάνει την αλλαγή της κατάστασης των μηνυμάτων στον διακομιστή. Η εντολή store()
είναι το κύριο εργαλείο σας για αυτό.
Επισήμανση Email (Αναγνωσμένα, Μη Αναγνωσμένα, Με Σημαία)
Μπορείτε να προσθέσετε, να αφαιρέσετε ή να αντικαταστήσετε σημαίες σε ένα μήνυμα. Η πιο κοινή σημαία είναι η \Seen
, η οποία ελέγχει την κατάσταση αναγνωσμένου/μη αναγνωσμένου.
- Σήμανση ως Αναγνωσμένο:
conn.store(msg_id, '+FLAGS', '\Seen')
- Σήμανση ως Μη Αναγνωσμένο:
conn.store(msg_id, '-FLAGS', '\Seen')
- Σήμανση/Αστεράκι σε Email:
conn.store(msg_id, '+FLAGS', '\Flagged')
- Αποσήμανση Email:
conn.store(msg_id, '-FLAGS', '\Flagged')
Αντιγραφή και Μετακίνηση Email
Δεν υπάρχει άμεση εντολή "μετακίνησης" στο IMAP. Η μετακίνηση ενός email είναι μια διαδικασία δύο βημάτων:
- Αντιγράψτε το μήνυμα στη θυρίδα προορισμού χρησιμοποιώντας το
conn.copy()
. - Επισημάνετε το αρχικό μήνυμα για διαγραφή χρησιμοποιώντας τη σημαία
\Deleted
.
# Assuming `msg_id` is the ID of the email to move
# 1. Copy to the 'Archive' mailbox
status, _ = conn.copy(msg_id, 'Archive')
if status == 'OK':
print(f"Message {msg_id.decode()} copied to Archive.")
# 2. Mark the original for deletion
conn.store(msg_id, '+FLAGS', '\Deleted')
print(f"Message {msg_id.decode()} marked for deletion.")
Μόνιμη Διαγραφή Email
Η επισήμανση ενός μηνύματος με \Deleted
δεν το αφαιρεί αμέσως. Απλώς το κρύβει από την προβολή στους περισσότερους πελάτες email. Για να αφαιρέσετε μόνιμα όλα τα μηνύματα στην επιλεγμένη θυρίδα που έχουν επισημανθεί για διαγραφή, πρέπει να καλέσετε τη μέθοδο expunge()
.
Προειδοποίηση: Το expunge()
είναι μη αναστρέψιμο. Μόλις κληθεί, τα δεδομένα χάνονται για πάντα.
# This will permanently delete all messages with the \Deleted flag
status, response = conn.expunge()
if status == 'OK':
print(f"{len(response)} messages expunged (permanently deleted).")
Μια κρίσιμη παρενέργεια του expunge()
είναι ότι μπορεί να ανα-αριθμήσει τα αναγνωριστικά μηνυμάτων για όλα τα επόμενα μηνύματα στη θυρίδα. Για αυτόν τον λόγο, είναι καλύτερο να αναγνωρίσετε όλα τα μηνύματα που θέλετε να επεξεργαστείτε, να εκτελέσετε τις ενέργειές σας (όπως αντιγραφή και επισήμανση για διαγραφή) και στη συνέχεια να καλέσετε το expunge()
μία φορά στο τέλος της συνεδρίας σας.
Συνδυάζοντας Όλα Μαζί: Ένα Πρακτικό Παράδειγμα
Ας δημιουργήσουμε ένα πλήρες σενάριο που εκτελεί μια πραγματική εργασία: Σάρωση των εισερχομένων για μη αναγνωσμένα email από το "invoices@mycorp.com", λήψη τυχόν συνημμένων PDF και μετακίνηση του επεξεργασμένου email σε μια θυρίδα με το όνομα "Processed-Invoices".
import imaplib
import email
from email.header import decode_header
import os
# --- Configuration ---
EMAIL_ACCOUNT = "your_email@example.com"
APP_PASSWORD = "your_16_digit_app_password"
IMAP_SERVER = "imap.gmail.com"
TARGET_SENDER = "invoices@mycorp.com"
DESTINATION_MAILBOX = "Processed-Invoices"
DOWNLOAD_DIR = "invoices"
# Create download directory if it doesn't exist
if not os.path.isdir(DOWNLOAD_DIR):
os.mkdir(DOWNLOAD_DIR)
def decode_email_header(header):
# (Same function as defined earlier)
decoded_parts = decode_header(header)
header_str = ""
for part, encoding in decoded_parts:
if isinstance(part, bytes):
header_str += part.decode(encoding or 'utf-8')
else:
header_str += part
return header_str
conn = None
try:
# --- Connect and Login ---
conn = imaplib.IMAP4_SSL(IMAP_SERVER)
conn.login(EMAIL_ACCOUNT, APP_PASSWORD)
print("Login successful.")
# --- Select INBOX ---
conn.select('INBOX')
print("INBOX selected.")
# --- Search for emails ---
search_criteria = f'(UNSEEN FROM "{TARGET_SENDER}")'
status, message_ids = conn.search(None, search_criteria)
if status != 'OK':
raise Exception("Search failed")
email_id_list = message_ids[0].split()
if not email_id_list:
print("No new invoices found.")
else:
print(f"Found {len(email_id_list)} new invoices to process.")
# --- Process Each Email ---
for email_id in email_id_list:
print(f"\nProcessing email ID: {email_id.decode()}")
# Fetch the email
status, msg_data = conn.fetch(email_id, '(RFC822)')
if status != 'OK':
print(f"Failed to fetch email ID {email_id.decode()}")
continue
raw_email = msg_data[0][1]
email_message = email.message_from_bytes(raw_email)
subject = decode_email_header(email_message["Subject"])
print(f" Subject: {subject}")
# Look for attachments
for part in email_message.walk():
if part.get_content_maintype() == 'multipart':
continue
if part.get('Content-Disposition') is None:
continue
filename = part.get_filename()
if filename and filename.lower().endswith('.pdf'):
decoded_filename = decode_email_header(filename)
filepath = os.path.join(DOWNLOAD_DIR, decoded_filename)
# Save the attachment
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
print(f" -> Downloaded attachment: {decoded_filename}")
# --- Move the processed email ---
# 1. Copy to destination mailbox
status, _ = conn.copy(email_id, DESTINATION_MAILBOX)
if status == 'OK':
# 2. Mark original for deletion
conn.store(email_id, '+FLAGS', '\Deleted')
print(f" Email moved to '{DESTINATION_MAILBOX}'.")
# --- Expunge and Clean Up ---
if email_id_list:
conn.expunge()
print("\nExpunged deleted emails.")
except Exception as e:
print(f"An error occurred: {e}")
finally:
if conn:
conn.logout()
print("Logged out.")
Βέλτιστες Πρακτικές και Χειρισμός Σφαλμάτων
Κατά την κατασκευή ισχυρών σεναρίων αυτοματισμού, λάβετε υπόψη τις ακόλουθες βέλτιστες πρακτικές:
- Ισχυρός Χειρισμός Σφαλμάτων: Περιβάλλετε τον κώδικά σας σε μπλοκ
try...except
για να εντοπίσετε πιθανά προβλήματα όπως αποτυχίες σύνδεσης (imaplib.IMAP4.error
), προβλήματα δικτύου ή σφάλματα ανάλυσης. - Διαχείριση Διαμόρφωσης: Μην κωδικοποιείτε ποτέ σκληρά διαπιστευτήρια. Χρησιμοποιήστε μεταβλητές περιβάλλοντος (
os.getenv()
), ένα αρχείο διαμόρφωσης (π.χ. INI ή YAML) ή έναν ειδικό διαχειριστή μυστικών. - Καταγραφή (Logging): Αντί για δηλώσεις
print()
, χρησιμοποιήστε την ενότηταlogging
της Python. Σας επιτρέπει να ελέγχετε την περιεκτικότητα της εξόδου σας, να γράφετε σε αρχεία και να προσθέτετε χρονοσφραγίδες, κάτι που είναι ανεκτίμητο για τον εντοπισμό σφαλμάτων σε σενάρια που εκτελούνται χωρίς επίβλεψη. - Περιορισμός Ρυθμού (Rate Limiting): Να είστε καλός πολίτης του διαδικτύου. Μην ελέγχετε τον διακομιστή email υπερβολικά. Εάν πρέπει να ελέγχετε συχνά για νέα αλληλογραφία, εξετάστε διαστήματα αρκετών λεπτών αντί για δευτερόλεπτα.
- Κωδικοποιήσεις Χαρακτήρων: Το email είναι ένα παγκόσμιο πρότυπο και θα συναντήσετε διάφορες κωδικοποιήσεις χαρακτήρων. Προσπαθείτε πάντα να καθορίζετε το charset από το μέρος του email (
part.get_content_charset()
) και να έχετε ένα εφεδρικό (όπως 'utf-8') για να αποφύγετε τοUnicodeDecodeError
.
Συμπέρασμα
Έχετε τώρα ταξιδέψει σε ολόκληρο τον κύκλο ζωής της αλληλεπίδρασης με έναν διακομιστή email χρησιμοποιώντας το imaplib
της Python. Έχουμε καλύψει τη δημιουργία μιας ασφαλούς σύνδεσης, την καταγραφή θυρίδων, την εκτέλεση ισχυρών αναζητήσεων, την ανάκτηση και ανάλυση πολύπλοκων πολυμερών email, τη λήψη συνημμένων και τη διαχείριση καταστάσεων μηνυμάτων στον διακομιστή.
Η δύναμη αυτής της γνώσης είναι τεράστια. Μπορείτε να δημιουργήσετε συστήματα για την αυτόματη κατηγοριοποίηση αιτημάτων υποστήριξης, την ανάλυση δεδομένων από καθημερινές αναφορές, την αρχειοθέτηση ενημερωτικών δελτίων, την ενεργοποίηση ενεργειών βάσει email ειδοποιήσεων και πολλά άλλα. Τα εισερχόμενα, κάποτε πηγή χειρωνακτικής εργασίας, μπορούν να γίνουν μια ισχυρή, αυτοματοποιημένη πηγή δεδομένων για τις εφαρμογές και τις ροές εργασίας σας.
Ποιες εργασίες email θα αυτοματοποιήσετε πρώτα; Οι δυνατότητες περιορίζονται μόνο από τη φαντασία σας. Ξεκινήστε μικρά, βασιστείτε στα παραδείγματα αυτού του οδηγού και ανακτήστε τον χρόνο σας από τα βάθη των εισερχομένων σας.