Python'un 'email' paketinin potansiyelini ortaya çıkarın. Karmaşık MIME mesajları oluşturmayı ve gelen e-postaları veri çıkarmak için etkili ve küresel olarak ayrıştırmayı öğrenin.
Python'un Email Paketinde Uzmanlaşmak: MIME Mesajı Oluşturma ve Sağlam Ayrıştırma Sanatı
E-posta, kişisel yazışmalar, iş operasyonları ve otomatik sistem bildirimleri için vazgeçilmez olan küresel iletişimin temel taşlarından biri olmaya devam etmektedir. Her zengin metinli e-postanın, her ekin ve özenle biçimlendirilmiş her imzanın arkasında Çok Amaçlı İnternet Posta Uzantıları'nın (MIME) karmaşıklığı yatar. Geliştiriciler, özellikle de Python ile çalışanlar için, bu MIME mesajlarını programatik olarak nasıl oluşturacaklarını ve ayrıştıracaklarını öğrenmek kritik bir beceridir.
Python'un yerleşik email
paketi, e-posta mesajlarını işlemek için sağlam ve kapsamlı bir çerçeve sunar. Sadece basit metin göndermek için değil; MIME'ın karmaşık detaylarını soyutlamak, sofistike e-postalar oluşturmanıza ve gelen e-postalardan belirli verileri olağanüstü bir hassasiyetle çıkarmanıza olanak tanımak için tasarlanmıştır. Bu kılavuz, sizi bu paketin iki ana yönü olan göndermek için MIME mesajları oluşturma ve veri çıkarmak için bunları ayrıştırma konularında derinlemesine bir yolculuğa çıkaracak ve en iyi uygulamalar hakkında küresel bir bakış açısı sunacaktır.
Hem oluşturmayı hem de ayrıştırmayı anlamak çok önemlidir. Bir mesaj oluşturduğunuzda, aslında başka bir sistemin yorumlaması için onun yapısını ve içeriğini tanımlarsınız. Ayrıştırdığınızda ise, başka bir sistem tarafından tanımlanan bir yapıyı ve içeriği yorumlarsınız. Birini derinlemesine anlamak, diğerinde ustalaşmaya büyük ölçüde yardımcı olur ve daha dayanıklı ve birlikte çalışabilir e-posta uygulamalarına yol açar.
MIME'ı Anlamak: Modern E-postanın Omurgası
Python'a özgü konulara dalmadan önce, MIME'ın ne olduğunu ve neden bu kadar hayati olduğunu kavramak önemlidir. Başlangıçta, e-posta mesajları düz metinle (7-bit ASCII karakterleri) sınırlıydı. 1990'ların başında tanıtılan MIME, e-postanın yeteneklerini aşağıdakileri destekleyecek şekilde genişletti:
- ASCII dışı karakterler: Arapça, Çince, Rusça gibi dillerde veya ASCII seti dışındaki karakterleri kullanan diğer dillerde metinlere izin verir.
- Ekler: Belgeler, resimler, ses ve video gibi dosyaların gönderilmesi.
- Zengin metin biçimlendirme: Kalın, italik, renkler ve düzenler içeren HTML e-postaları.
- Çoklu parçalar: Düz metin, HTML ve ekleri tek bir e-posta içinde birleştirme.
MIME bunu, bir e-posta mesajına belirli başlıklar ekleyerek ve gövdesini çeşitli "parçalara" yapılandırarak başarır. Karşılaşacağınız temel MIME başlıkları şunlardır:
Content-Type:
Bir parçadaki veri türünü belirtir (ör.text/plain
,text/html
,image/jpeg
,application/pdf
,multipart/alternative
). Genellikle bircharset
parametresi de içerir (ör.charset=utf-8
).Content-Transfer-Encoding:
E-posta istemcisinin içeriği nasıl çözmesi gerektiğini belirtir (ör. ikili veriler içinbase64
, çoğunlukla metin olup bazı ASCII dışı karakterler içerenler içinquoted-printable
).Content-Disposition:
Alıcının e-posta istemcisinin parçayı nasıl görüntülemesi gerektiğini önerir (ör. mesaj gövdesi içinde görüntülemek içininline
, kaydedilecek bir dosya içinattachment
).
Python email
Paketi: Derinlemesine Bir Bakış
Python'un email
paketi, e-posta mesajlarını programatik olarak oluşturmak, ayrıştırmak ve değiştirmek için tasarlanmış kapsamlı bir kütüphanedir. Bir e-postanın yapısını temsil eden Message
nesneleri konsepti etrafında inşa edilmiştir.
Paket içindeki anahtar modüller şunları içerir:
email.message:
E-posta mesajları oluşturmak ve işlemek için birincil arayüz olan çekirdekEmailMessage
sınıfını içerir. MIME ayrıntılarını otomatik olarak ele alan son derece esnek bir sınıftır.email.mime:
MIME yapısı üzerinde daha açık kontrol sunan eski sınıfları (MIMEText
,MIMEMultipart
gibi) sağlar.EmailMessage
basitliği nedeniyle genellikle yeni kodlar için tercih edilse de, bu sınıfları anlamak faydalı olabilir.email.parser:
Ham e-posta verilerini (bayt veya dize)EmailMessage
nesnelerine dönüştürmek içinBytesParser
veParser
gibi sınıflar sunar.email.policy:
E-posta mesajlarının nasıl oluşturulduğunu ve ayrıştırıldığını kontrol eden politikaları tanımlar, başlık kodlamasını, satır sonlarını ve hata işlemeyi etkiler.
Çoğu modern kullanım durumu için, hem oluşturma hem de ayrıştırılmış bir mesaj nesnesi için öncelikle email.message.EmailMessage
sınıfıyla etkileşimde bulunacaksınız. Yöntemleri, eskiden eski email.mime
sınıflarıyla daha ayrıntılı olan bir süreci büyük ölçüde basitleştirir.
MIME Mesajı Oluşturma: E-postaları Hassasiyetle İnşa Etmek
E-posta oluşturmak, çeşitli bileşenleri (metin, HTML, ekler) geçerli bir MIME yapısında bir araya getirmeyi içerir. EmailMessage
sınıfı bu süreci önemli ölçüde kolaylaştırır.
Basit Metin E-postaları
En basit e-posta düz metindir. Bir tane oluşturabilir ve temel başlıkları zahmetsizce ayarlayabilirsiniz:
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Greetings from Python'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Hello, this is a plain text email sent from Python.\n\nBest regards,\nYour Python Script')
print(msg.as_string())
Açıklama:
EmailMessage()
boş bir mesaj nesnesi oluşturur.- Sözlük benzeri erişim (
msg['Subject'] = ...
) yaygın başlıkları ayarlar. set_content()
e-postanın birincil içeriğini ekler. Varsayılan olarak,Content-Type: text/plain; charset="utf-8"
sonucunu çıkarır.as_string()
mesajı SMTP aracılığıyla göndermeye veya bir dosyaya kaydetmeye uygun bir dize biçimine serileştirir.
HTML İçeriği Ekleme
Bir HTML e-postası göndermek için, set_content()
çağrısı yaparken içerik türünü belirtmeniz yeterlidir. E-posta istemcileri HTML göstermeyen alıcılar için veya erişilebilirlik nedenleriyle düz metin alternatifi sağlamak iyi bir uygulamadır.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Your HTML Newsletter'
msg['From'] = 'newsletter@example.com'
msg['To'] = 'subscriber@example.com'
html_content = """
<html>
<head></head>
<body>
<h1>Welcome to Our Global Update!</h1>
<p>Dear Subscriber,</p>
<p>This is your <strong>latest update</strong> from around the world.</p>
<p>Visit our <a href="http://www.example.com">website</a> for more.</p>
<p>Best regards,<br>The Team</p>
</body>
</html>
"""
# HTML sürümünü ekle
msg.add_alternative(html_content, subtype='html')
# Düz metin yedeği ekle
plain_text_content = (
"Welcome to Our Global Update!\n\n"
"Dear Subscriber,\n\n"
"This is your latest update from around the world.\n"
"Visit our website for more: http://www.example.com\n\n"
"Best regards,\nThe Team"
)
msg.add_alternative(plain_text_content, subtype='plain')
print(msg.as_string())
Açıklama:
add_alternative()
, *aynı* içeriğin farklı temsillerini eklemek için kullanılır. E-posta istemcisi, işleyebileceği "en iyi" olanı (genellikle HTML) görüntüler.- Bu, otomatik olarak bir
multipart/alternative
MIME yapısı oluşturur.
Ekleri İşleme
Dosya eklemek add_attachment()
kullanarak basittir. Her tür dosyayı ekleyebilirsiniz ve paket uygun MIME türlerini ve kodlamaları (genellikle base64
) yönetir.
from email.message import EmailMessage
from pathlib import Path
# Gösterim için sahte dosyalar oluşturun
Path('report.pdf').write_bytes(b'%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n2 0 obj<</Count 0>>endobj\nxref\n0 3\n0000000000 65535 f\n0000000009 00000 n\n0000000052 00000 n\ntrailer<</Size 3/Root 1 0 R>>startxref\n104\n%%EOF') # Çok temel, geçersiz bir PDF yer tutucusu
Path('logo.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82') # 1x1 şeffaf PNG yer tutucusu
msg = EmailMessage()
msg['Subject'] = 'Important Document and Image'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Please find the attached report and company logo.')
# Bir PDF dosyası ekleyin
with open('report.pdf', 'rb') as f:
file_data = f.read()
msg.add_attachment(
file_data,
maintype='application',
subtype='pdf',
filename='Annual_Report_2024.pdf'
)
# Bir resim dosyası ekleyin
with open('logo.png', 'rb') as f:
image_data = f.read()
msg.add_attachment(
image_data,
maintype='image',
subtype='png',
filename='CompanyLogo.png'
)
print(msg.as_string())
# Sahte dosyaları temizleyin
Path('report.pdf').unlink()
Path('logo.png').unlink()
Açıklama:
add_attachment()
dosya içeriğinin ham baytlarını alır.maintype
vesubtype
MIME türünü belirtir (ör.application/pdf
,image/png
). Bunlar, alıcının e-posta istemcisinin eki doğru bir şekilde tanımlaması ve işlemesi için çok önemlidir.filename
, ekin alıcı tarafından kaydedileceği adı sağlar.- Bu, otomatik olarak bir
multipart/mixed
yapısı kurar.
Çok Parçalı Mesajlar Oluşturma
Hem HTML gövdesi, hem düz metin yedeği, hem de satır içi resimler veya ilgili dosyaları olan bir mesajınız olduğunda, daha karmaşık bir çok parçalı yapıya ihtiyacınız vardır. EmailMessage
sınıfı bunu add_related()
ve add_alternative()
ile akıllıca yönetir.
Yaygın bir senaryo, doğrudan HTML içine gömülü bir resim ("satır içi" resim) içeren bir HTML e-postasıdır. Bu, multipart/related
kullanır.
from email.message import EmailMessage
from pathlib import Path
# Gösterim için sahte bir resim dosyası oluşturun (1x1 şeffaf bir PNG)
Path('banner.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82')
msg = EmailMessage()
msg['Subject'] = 'Inline Image Example'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
# Düz metin sürümü (yedek)
plain_text = 'Check out our amazing banner!\n\n[Image: Banner.png]\n\nVisit our site.'
msg.set_content(plain_text, subtype='plain') # Başlangıçtaki düz metin içeriğini ayarlayın
# HTML sürümü (satır içi resim için CID ile)
html_content = """
<html>
<head></head>
<body>
<h1>Our Latest Offer!</h1>
<p>Dear Customer,</p>
<p>Don't miss out on our special global promotion:</p>
<img src="cid:my-banner-image" alt="Promotion Banner">
<p>Click <a href="http://www.example.com">here</a> to learn more.</p>
</body>
</html>
"""
msg.add_alternative(html_content, subtype='html') # HTML alternatifini ekleyin
# Satır içi resmi ekleyin (ilgili içerik)
with open('banner.png', 'rb') as img_file:
image_data = img_file.read()
msg.add_related(
image_data,
maintype='image',
subtype='png',
cid='my-banner-image' # Bu CID, HTML'deki 'src' ile eşleşir
)
print(msg.as_string())
# Sahte dosyayı temizleyin
Path('banner.png').unlink()
Açıklama:
set_content()
başlangıç içeriğini (burada, düz metin) oluşturur.add_alternative()
HTML sürümünü ekleyerek düz metin ve HTML parçalarını içeren birmultipart/alternative
yapısı oluşturur.add_related()
, mesaj parçalarından biriyle "ilgili" olan içerik için, genellikle HTML'deki satır içi resimler için kullanılır. Bircid
(İçerik-ID) parametresi alır, bu daha sonra HTML<img src="cid:my-banner-image">
etiketinde referans gösterilir.- Nihai yapı,
multipart/alternative
parçasını içeren birmultipart/mixed
(eğer harici ekler olsaydı) olacaktır, bu da sırayla birmultipart/related
parçası içerir.multipart/related
parçası HTML'yi ve satır içi resmi içerir.EmailMessage
sınıfı bu iç içe geçmiş karmaşıklığı sizin için yönetir.
Küresel Erişim için Kodlama ve Karakter Setleri
Uluslararası iletişim için doğru karakter kodlaması çok önemlidir. email
paketi, varsayılan olarak, dünyanın dört bir yanından çeşitli karakter setlerini işlemek için evrensel standart olan UTF-8'i kullanma konusunda oldukça kararlıdır.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Global Characters: こんにちは, Привет, नमस्ते'
msg['From'] = 'global_sender@example.com'
msg['To'] = 'global_recipient@example.com'
# Japonca, Rusça ve Hintçe karakterler
content = "This message contains diverse global characters:\n"
content += "こんにちは (Japanese)\n"
content += "Привет (Russian)\n"
content += "नमस्ते (Hindi)\n\n"
content += "The 'email' package handles UTF-8 gracefully."
msg.set_content(content)
print(msg.as_string())
Açıklama:
set_content()
bir Python dizesi aldığında, onu otomatik olarak UTF-8'e kodlar veContent-Type: text/plain; charset="utf-8"
başlığını ayarlar.- İçerik gerektiriyorsa (ör. birçok ASCII dışı karakter içeriyorsa), eski e-posta sistemleri üzerinden güvenli iletimi sağlamak için
Content-Transfer-Encoding: quoted-printable
veyabase64
de uygulayabilir. Paket bunu seçilen politikaya göre otomatik olarak yönetir.
Özel Başlıklar ve Politikalar
Bir e-postaya herhangi bir özel başlık ekleyebilirsiniz. Politikalar (email.policy
'den) mesajların nasıl işlendiğini tanımlar, başlık kodlaması, satır sonları ve hata işleme gibi yönleri etkiler. Varsayılan politika genellikle iyidir, ancak sıkı SMTP uyumluluğu için `SMTP` seçebilir veya özel olanları tanımlayabilirsiniz.
from email.message import EmailMessage
from email import policy
msg = EmailMessage(policy=policy.SMTP)
msg['Subject'] = 'Email with Custom Header'
msg['From'] = 'info@example.org'
msg['To'] = 'user@example.org'
msg['X-Custom-Header'] = 'This is a custom value for tracking'
msg['Reply-To'] = 'support@example.org'
msg.set_content('This email demonstrates custom headers and policies.')
print(msg.as_string())
Açıklama:
policy=policy.SMTP
kullanmak, SMTP standartlarına sıkı sıkıya uyulmasını sağlar, bu da teslim edilebilirlik için kritik olabilir.- Özel başlıklar, standart olanlar gibi eklenir. Genellikle standart dışı başlıkları belirtmek için
X-
ile başlarlar.
MIME Mesajı Ayrıştırma: Gelen E-postalardan Bilgi Çıkarma
Ayrıştırma, ham e-posta verilerini (genellikle IMAP aracılığıyla veya bir dosyadan alınan) alıp, daha sonra kolayca inceleyip değiştirebileceğiniz bir `EmailMessage` nesnesine dönüştürmeyi içerir.
Yükleme ve Başlangıç Ayrıştırması
E-postaları genellikle ham bayt olarak alırsınız. Bunun için email.parser.BytesParser
(veya kolaylık fonksiyonları olan email.message_from_bytes()
) kullanılır.
from email.parser import BytesParser
from email.policy import default
raw_email_bytes = b"""
From: sender@example.com
To: recipient@example.com
Subject: Test Email with Basic Headers
Date: Mon, 1 Jan 2024 10:00:00 +0000
Content-Type: text/plain; charset="utf-8"
This is the body of the email.
It's a simple test.
"""
# BytesParser kullanarak
parser = BytesParser(policy=default)
msg = parser.parsebytes(raw_email_bytes)
# Veya kolaylık fonksiyonunu kullanarak
# from email import message_from_bytes
# msg = message_from_bytes(raw_email_bytes, policy=default)
print(f"Subject: {msg['subject']}")
print(f"From: {msg['from']}")
print(f"Content-Type: {msg['Content-Type']}")
Açıklama:
BytesParser
ham bayt verilerini (e-postaların iletildiği şekil) alır ve birEmailMessage
nesnesi döndürür.policy=default
ayrıştırma kurallarını belirtir.
Başlıklara Erişme
Başlıklara sözlük benzeri anahtarlarla kolayca erişilebilir. Paket, kodlanmış başlıkların (örneğin, uluslararası karakterler içeren konular) kod çözümünü otomatik olarak yapar.
# ... (önceki ayrıştırma örneğindeki 'msg' nesnesini kullanarak)
print(f"Date: {msg['date']}")
print(f"Message ID: {msg['Message-ID'] if 'Message-ID' in msg else 'N/A'}")
# Birden çok başlığı işleme (örneğin, 'Received' başlıkları)
# from email.message import EmailMessage # Henüz içe aktarılmadıysa
# from email import message_from_string # Hızlı bir dize örneği için
multi_header_email = message_from_string(
"""
From: a@example.com
To: b@example.com
Subject: Multi-header Test
Received: from client.example.com (client.example.com [192.168.1.100])
by server.example.com (Postfix) with ESMTP id 123456789
for <b@example.com>; Mon, 1 Jan 2024 10:00:00 +0000 (GMT)
Received: from mx.another.com (mx.another.com [192.168.1.101])
by server.example.com (Postfix) with ESMTP id 987654321
for <b@example.com>; Mon, 1 Jan 2024 09:59:00 +0000 (GMT)
Body content here.
"""
)
received_headers = multi_header_email.get_all('received')
if received_headers:
print("\nReceived Headers:")
for header in received_headers:
print(f"- {header}")
Açıklama:
- Bir başlığa erişmek, değerini bir dize olarak döndürür.
get_all('header-name')
, birden çok kez görünebilen başlıklar (Received
gibi) için kullanışlıdır.- Paket, başlık kod çözmeyi yönetir, bu nedenle
Subject: =?utf-8?Q?Global_Characters:_=E3=81=93=E3=82=93=E3=81=AB=E3=81=A1=E3=81=AF?=
gibi değerler otomatik olarak okunabilir dizelere dönüştürülür.
Gövde İçeriğini Çıkarma
Gerçek mesaj gövdesini çıkarmak, mesajın çok parçalı olup olmadığını kontrol etmeyi gerektirir. Çok parçalı mesajlar için, parçaları arasında dolaşırsınız.
from email.message import EmailMessage
from email import message_from_string
multipart_email_raw = """
From: multi@example.com
To: user@example.com
Subject: Test Multipart Email
Content-Type: multipart/alternative; boundary="_----------=_12345"
--_----------=_12345
Content-Type: text/plain; charset="utf-8"
Hello from the plain text part!
--_----------=_12345
Content-Type: text/html; charset="utf-8"
<html>
<body>
<h1>Hello from the HTML part!</h1>
<p>This is a <strong>rich text</strong> email.</p>
</body>
</html>
--_----------=_12345--
"""
msg = message_from_string(multipart_email_raw)
if msg.is_multipart():
print("\n--- Multipart Email Body ---")
for part in msg.iter_parts():
content_type = part.get_content_type()
charset = part.get_content_charset() or 'utf-8' # Belirtilmemişse varsayılan olarak utf-8
payload = part.get_payload(decode=True) # Yük baytlarını çöz
try:
decoded_content = payload.decode(charset)
print(f"Content-Type: {content_type}, Charset: {charset}\nContent:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Content-Type: {content_type}, Charset: {charset}\nContent: (Binary or undecodable data)\n")
# İkili verileri işle veya yedek bir kodlama dene
else:
print("\n--- Single Part Email Body ---")
charset = msg.get_content_charset() or 'utf-8'
payload = msg.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
print(f"Content-Type: {msg.get_content_type()}, Charset: {charset}\nContent:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Content: (Binary or undecodable data)\n")
Açıklama:
is_multipart()
, e-postanın birden çok parçası olup olmadığını belirler.iter_parts()
, çok parçalı bir mesajın tüm alt parçaları arasında dolaşır.get_content_type()
tam MIME türünü döndürür (ör.text/plain
).get_content_charset()
,Content-Type
başlığından karakter setini çıkarır.get_payload(decode=True)
çok önemlidir: *çözülmüş* içeriği bayt olarak döndürür. Daha sonra bu baytları doğru karakter setini kullanarak.decode()
yapmanız ve bir Python dizesi elde etmeniz gerekir.
Ayrıştırma Sırasında Ekleri İşleme
Ekler de çok parçalı bir mesajın parçalarıdır. Onları Content-Disposition
başlıklarını kullanarak tanımlayabilir ve çözülmüş yüklerini kaydedebilirsiniz.
from email.message import EmailMessage
from email import message_from_string
import os
# Basit bir ek içeren örnek e-posta
email_with_attachment = """
From: attach@example.com
To: user@example.com
Subject: Document Attached
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="_----------=_XYZ"
--_----------=_XYZ
Content-Type: text/plain; charset="utf-8"
Here is your requested document.
--_----------=_XYZ
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="document.pdf"
JVBERi0xLjQKMSAwIG9iagpbL1BERi9UZXh0L0ltYWdlQy9JbWFnZUkvSW1hZ0VCXQplbmRvYmoK
--_----------=_XYZ--
"""
msg = message_from_string(email_with_attachment)
output_dir = 'parsed_attachments'
os.makedirs(output_dir, exist_ok=True)
print("\n--- Processing Attachments ---")
for part in msg.iter_attachments():
filename = part.get_filename()
if filename:
filepath = os.path.join(output_dir, filename)
try:
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
print(f"Saved attachment: {filepath} (Type: {part.get_content_type()})")
except Exception as e:
print(f"Error saving {filename}: {e}")
else:
print(f"Found an attachment without a filename (Content-Type: {part.get_content_type()})")
# Çıktı dizinini temizle
# import shutil
# shutil.rmtree(output_dir)
Açıklama:
iter_attachments()
, özellikle ek olma olasılığı yüksek olan parçaları (yani,Content-Disposition: attachment
başlığına sahip olan veya başka şekilde sınıflandırılmamış olanları) verir.get_filename()
, dosya adınıContent-Disposition
başlığından çıkarır.part.get_payload(decode=True)
, ekin ham ikili içeriğini,base64
veyaquoted-printable
'dan zaten çözülmüş olarak alır.
Kodlamaları ve Karakter Setlerini Çözme
email
paketi, get_payload(decode=True)
çağırdığınızda yaygın aktarım kodlamalarını (base64
, quoted-printable
gibi) otomatik olarak çözmede mükemmel bir iş çıkarır. Metin içeriği için ise, Content-Type
başlığında belirtilen charset
'i kullanmaya çalışır. Eğer bir karakter seti belirtilmemişse veya geçersizse, bunu zarif bir şekilde ele almanız gerekebilir.
from email.message import EmailMessage
from email import message_from_string
# Potansiyel olarak sorunlu bir karakter seti ile örnek
email_latin1 = """
From: legacy@example.com
To: new_system@example.com
Subject: Special characters: àéíóú
Content-Type: text/plain; charset="iso-8859-1"
This message contains Latin-1 characters: àéíóú
"""
msg = message_from_string(email_latin1)
if msg.is_multipart():
for part in msg.iter_parts():
payload = part.get_payload(decode=True)
charset = part.get_content_charset() or 'utf-8'
try:
print(f"Decoded (Charset: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Failed to decode with {charset}. Trying fallback...")
# Yaygın bir karakter setine veya bekleniyorsa 'latin-1'e geri dön
print(f"Decoded (Fallback Latin-1): {payload.decode('latin-1', errors='replace')}")
else:
payload = msg.get_payload(decode=True)
charset = msg.get_content_charset() or 'utf-8'
try:
print(f"Decoded (Charset: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Failed to decode with {charset}. Trying fallback...")
print(f"Decoded (Fallback Latin-1): {payload.decode('latin-1', errors='replace')}")
Açıklama:
- Her zaman
Content-Type
başlığında belirtilen karakter setini kullanmaya çalışın. - Özellikle çeşitli ve potansiyel olarak standart dışı kaynaklardan gelen e-postalarla uğraşırken sağlamlık için bir
try-except UnicodeDecodeError
bloğu kullanın. errors='replace'
veyaerrors='ignore'
,.decode()
ile birlikte kullanılarak hedef kodlamaya eşlenemeyen karakterleri işleyebilir ve çökmeleri önleyebilir.
İleri Düzey Ayrıştırma Senaryoları
Gerçek dünyadaki e-postalar, iç içe geçmiş çok parçalı yapılarla oldukça karmaşık olabilir. email
paketinin özyinelemeli doğası, bunlarda gezinmeyi kolaylaştırır. Derinlemesine iç içe geçmiş mesajları dolaşmak için is_multipart()
ile iter_parts()
'ı birleştirebilirsiniz.
from email.message import EmailMessage
from email import message_from_string
def parse_email_part(part, indent=0):
prefix = " " * indent
content_type = part.get_content_type()
charset = part.get_content_charset() or 'N/A'
print(f"{prefix}Part Type: {content_type}, Charset: {charset}")
if part.is_multipart():
for subpart in part.iter_parts():
parse_email_part(subpart, indent + 1)
elif part.get_filename(): # Bu bir ek
print(f"{prefix} Attachment: {part.get_filename()} (Size: {len(part.get_payload(decode=True))} bytes)")
else: # Bu normal bir metin/html gövde parçası
payload = part.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
# print(f"{prefix} Content (first 100 chars): {decoded_content[:100]}...") # Kısalık için
except UnicodeDecodeError:
print(f"{prefix} Content: (Binary or undecodable text)")
complex_email_raw = """
From: complex@example.com
To: receiver@example.com
Subject: Complex Email with HTML, Plain, and Attachment
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="outer_boundary"
--outer_boundary
Content-Type: multipart/alternative; boundary="inner_boundary"
--inner_boundary
Content-Type: text/plain; charset="utf-8"
Plain text content.
--inner_boundary
Content-Type: text/html; charset="utf-8"
<html><body><h2>HTML Content</h2></body></html>
--inner_boundary--
--outer_boundary
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="data.bin"
SGVsbG8gV29ybGQh
--outer_boundary--
"""
msg = message_from_string(complex_email_raw)
print("\n--- Traversing Complex Email Structure ---")
parse_email_part(msg)
Açıklama:
- Özyinelemeli
parse_email_part
fonksiyonu, tüm mesaj ağacında nasıl gezinileceğini, her seviyede çok parçalı parçaları, ekleri ve gövde içeriğini nasıl tanımlayacağınızı gösterir. - Bu desen, derinlemesine iç içe geçmiş e-postalardan belirli türdeki içerikleri çıkarmak için son derece esnektir.
Oluşturma ve Ayrıştırma: Karşılaştırmalı Bir Bakış Açısı
Farklı işlemler olmalarına rağmen, oluşturma ve ayrıştırma aynı madalyonun iki yüzüdür: MIME mesaj işleme. Birini anlamak kaçınılmaz olarak diğerine yardımcı olur.
Oluşturma (Gönderme):
- Odak: Başlıkları, içeriği ve ekleri standartlara uygun bir MIME yapısında doğru bir şekilde bir araya getirmek.
- Birincil Araç:
set_content()
,add_attachment()
,add_alternative()
,add_related()
gibi yöntemlerleemail.message.EmailMessage
. - Temel Zorluklar: Doğru MIME türlerini, karakter setlerini (özellikle küresel destek için UTF-8), `Content-Transfer-Encoding`'i ve uygun başlık biçimlendirmesini sağlamak. Hatalı adımlar, e-postaların doğru görüntülenmemesine, eklerin bozulmasına veya mesajların spam olarak işaretlenmesine yol açabilir.
Ayrıştırma (Alma):
- Odak: Ham bir e-posta bayt akışını bileşen parçalarına ayırmak, belirli başlıkları, gövde içeriğini ve ekleri çıkarmak.
- Birincil Araç:
email.parser.BytesParser
veyaemail.message_from_bytes()
, ardından ortaya çıkanEmailMessage
nesnesindeis_multipart()
,iter_parts()
,get_payload()
,get_filename()
gibi yöntemler ve başlık erişimi ile gezinmek. - Temel Zorluklar: Hatalı biçimlendirilmiş e-postaları işlemek, karakter kodlamalarını doğru bir şekilde tanımlamak (özellikle belirsiz olduğunda), eksik başlıklarla başa çıkmak ve çeşitli MIME yapılarından verileri sağlam bir şekilde çıkarmak.
`EmailMessage` kullanarak oluşturduğunuz bir mesaj, `BytesParser` tarafından mükemmel bir şekilde ayrıştırılabilmelidir. Benzer şekilde, ayrıştırma sırasında üretilen MIME yapısını anlamak, karmaşık mesajları kendiniz nasıl oluşturacağınıza dair size bir fikir verir.
Python ile Küresel E-posta İşleme için En İyi Uygulamalar
Küresel bir kitleyle etkileşimde bulunan veya çeşitli e-posta kaynaklarını işleyen uygulamalar için şu en iyi uygulamaları göz önünde bulundurun:
- UTF-8'i Standartlaştırın: Hem oluştururken hem de ayrıştırırken beklerken tüm metin içeriği için daima UTF-8 kullanın. Bu, karakter kodlaması için küresel standarttır ve mojibake'yi (bozuk metin) önler.
- E-posta Adreslerini Doğrulayın: Göndermeden önce, teslim edilebilirliği sağlamak için alıcı e-posta adreslerini doğrulayın. Ayrıştırma sırasında, `From`, `To` veya `Cc` başlıklarında potansiyel olarak geçersiz veya hatalı biçimlendirilmiş adreslere hazırlıklı olun.
- Titizlikle Test Edin: HTML ve eklerin tutarlı bir şekilde görüntülenmesini sağlamak için e-posta oluşturma işleminizi çeşitli e-posta istemcileri (Gmail, Outlook, Apple Mail, Thunderbird) ve platformlarla test edin. Ayrıştırma için, alışılmadık kodlamalara, eksik başlıklara veya karmaşık iç içe yapılara sahip olanlar da dahil olmak üzere geniş bir örnek e-posta yelpazesiyle test edin.
- Ayrıştırılmış Girdiyi Temizleyin: Gelen e-postalardan çıkarılan içeriği her zaman güvenilmez olarak kabul edin. Bir web uygulamasında görüntülüyorsanız, XSS saldırılarını önlemek için HTML içeriğini temizleyin. Dosyaları kaydederken yol geçişi veya diğer güvenlik açıklarını önlemek için ek dosya adlarını ve türlerini doğrulayın.
- Sağlam Hata İşleme: Yükleri çözerken veya potansiyel olarak eksik başlıklara erişirken kapsamlı
try-except
blokları uygulayın.UnicodeDecodeError
veyaKeyError
'ı zarif bir şekilde ele alın. - Büyük Ekleri İşleyin: Hem oluştururken (posta sunucusu sınırlarını aşmamak için) hem de ayrıştırırken (aşırı bellek kullanımı veya disk alanı tüketimini önlemek için) ek boyutlarına dikkat edin. Sisteminiz destekliyorsa büyük ekleri akışla işlemeyi düşünün.
email.policy
'den Yararlanın: Kritik uygulamalar için, teslim edilebilirliği ve birlikte çalışabilirliği etkileyebilecek e-posta standartlarına sıkı uyumu sağlamak için açıkça bir `email.policy` (ör. `policy.SMTP`) seçin.- Meta Veri Koruma: Ayrıştırma sırasında, hangi meta verilerin (başlıklar, orijinal sınır dizeleri) korunmasının önemli olduğuna karar verin, özellikle bir posta arşivleme veya yönlendirme sistemi oluşturuyorsanız.
Sonuç
Python'un email
paketi, programatik olarak e-posta ile etkileşime girmesi gereken herkes için inanılmaz derecede güçlü ve esnek bir kütüphanedir. Hem MIME mesajlarının oluşturulmasında hem de gelen e-postaların sağlam bir şekilde ayrıştırılmasında ustalaşarak, sofistike e-posta otomasyon sistemleri oluşturma, e-posta istemcileri inşa etme, e-posta verilerini analiz etme ve e-posta işlevlerini neredeyse her uygulamaya entegre etme yeteneğinin kilidini açarsınız.
Paket, MIME'ın temel karmaşıklıklarını düşünceli bir şekilde ele alarak geliştiricilerin e-posta etkileşimlerinin içeriğine ve mantığına odaklanmalarını sağlar. İster küresel bir kitleye kişiselleştirilmiş bültenler gönderiyor olun, ister otomatik sistem raporlarından kritik veriler çıkarıyor olun, email
paketini derinlemesine anlamak, güvenilir, birlikte çalışabilir ve küresel farkındalığa sahip e-posta çözümleri oluşturmada paha biçilmez olacaktır.