Skip to content

Sending Email with Python

Python provides two common approaches to sending email: automating a browser-based webmail client with Selenium, or communicating directly with an SMTP server using the standard smtplib library. This article demonstrates both, covering plain-text email, HTML email, HTML with embedded local images, and various attachment types.

Sending 163 Mail via Selenium

The following example automates the 163 webmail interface using Selenium WebDriver to log in and send a message:

import time
import datetime
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait  # Wait for page elements to load
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

def login(user, pwd):
    """ Log in to 163 mailbox """
    # Because scan-to-login is the default, click the "password login" button first
    time.sleep(1)
    wait.until(EC.presence_of_element_located((By.ID, 'switchAccountLogin'))).click()
    # Switch into the login iframe (index 0)
    time.sleep(3)
    iframe = driver.find_elements_by_tag_name('iframe')
    driver.switch_to.frame(iframe[0])

    # Enter credentials
    time.sleep(1)
    driver.find_element_by_class_name('dlemail').send_keys(user)
    time.sleep(2)
    driver.find_element_by_class_name('dlpwd').send_keys(pwd)
    time.sleep(2)
    driver.find_element_by_id('dologin').click()


def send_mail():
    """ Send a 163 email — requires username, password, recipient, and content """

    try:
        # Step 1: log in
        login(user, pwd)

        # Step 2: click "Compose"
        wait.until(EC.presence_of_element_located((By.ID, '_mail_component_24_24'))).click()

        # Step 3: fill in recipient, subject, and body
        time.sleep(1)
        # 3.1 Recipient
        wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'nui-editableAddr-ipt'))).send_keys(addr)
        time.sleep(2)
        # 3.2 Subject
        title = driver.find_elements_by_class_name('nui-ipt-input')
        title[2].send_keys(theme)

        # 3.3 Switch into the content iframe and type the message body
        time.sleep(1)
        content_iframe = driver.find_element_by_class_name('APP-editor-iframe')
        driver.switch_to.frame(content_iframe)
        nui_scroll = wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'nui-scroll')))
        nui_scroll.send_keys(content)

        # Step 4: exit the content iframe, then click Send
        time.sleep(1)
        driver.switch_to.default_content()
        time.sleep(1)
        # The Send button is the third element with class "nui-btn-text"
        driver.find_elements_by_class_name('nui-btn-text')[2].click()

    finally:
        # Close the browser
        time.sleep(3)
        driver.quit()


if __name__ == '__main__':
    from getpass import getpass
    user = input("Email address: ").strip()
    pwd = getpass('Password: ')
    driver = webdriver.Chrome()
    wait = WebDriverWait(driver, 10)
    driver.get('https://mail.163.com/')

    addr = "[email protected]"     # Recipient
    theme = 'Hello from Python'        # Subject
    content = 'Test message sent at {}'.format(datetime.datetime.now())
    send_mail()

Sending Email via SMTP

Common Email Protocols

ProtocolPurpose
SMTPSending email from client to server, and between servers
POP3Downloading email from server to client
IMAPAccessing and managing email on the server
ExchangeMicrosoft enterprise email server
CardDAVContact synchronization protocol

For QQ Mail (recommended), enable SMTP access in Settings → Account → POP3/IMAP/SMTP and copy the generated authorization code. Use that code as the password in your script.

Sending Plain-Text Email

import smtplib
from email.mime.text import MIMEText
from email.header import Header

# SMTP server settings
mail_host = "smtp.qq.com"
mail_user = "[email protected]"
mail_pass = "your_authorization_code"   # Authorization code, not the QQ password
sender    = '[email protected]'
receivers = ['[email protected]']

send_content = 'Python email send test...'
message = MIMEText(send_content, 'plain', 'utf-8')  # plain text, UTF-8 encoding
message['From']    = Header("Sender Name", 'utf-8')
message['To']      = Header("Recipient Name", 'utf-8')
message['Subject'] = Header('Email subject', 'utf-8')

try:
    smtpObj = smtplib.SMTP()
    smtpObj.connect(mail_host, 25)   # Port 25 for SMTP
    smtpObj.login(mail_user, mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("Email sent successfully")
except smtplib.SMTPException:
    print("Error: could not send email")

Sending HTML Email

import smtplib
from email.mime.text import MIMEText
from email.header import Header

mail_host = "smtp.qq.com"
mail_user = "[email protected]"
mail_pass = "your_authorization_code"

sender    = '[email protected]'
receivers = ['[email protected]']

send_content = """
<h1>Hello</h1>
<p>This is an <strong>HTML</strong> email from Python.</p>
<a href="https://example.com">Visit example.com</a>
"""
message = MIMEText(send_content, 'html', 'utf-8')   # 'html' instead of 'plain'
message['From']    = Header("Sender Name", 'utf-8')
message['To']      = Header("Recipient Name", 'utf-8')
message['Subject'] = Header('HTML Email Test', 'utf-8')

try:
    smtpObj = smtplib.SMTP()
    smtpObj.connect(mail_host, 25)
    smtpObj.login(mail_user, mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("Email sent successfully")
except smtplib.SMTPException:
    print("Error: could not send email")

HTML Email with an Embedded Local Image

Embed a local image using a Content-ID header — the image data is attached as a MIME part and referenced in the HTML via cid::

import smtplib
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.header import Header

mail_host = "smtp.qq.com"
mail_user = "[email protected]"
mail_pass = "your_authorization_code"

sender    = '[email protected]'
receivers = ['[email protected]']

message = MIMEMultipart('related')
message['From']    = Header("Sender Name", 'utf-8')
message['To']      = Header("Recipient Name", 'utf-8')
message['Subject'] = Header('Email with Local Image', 'utf-8')

msg = MIMEMultipart('alternative')
message.attach(msg)

send_content = """
<h1>Embedded image below</h1>
<img src="cid:image">
"""
msg.attach(MIMEText(send_content, 'html', 'utf-8'))

# Read the local image and attach it with a Content-ID
with open('img.jpg', 'rb') as f:
    img_msg = MIMEImage(f.read())

img_msg.add_header('Content-ID', '<image>')   # Must match "cid:image" in the HTML
message.attach(img_msg)

try:
    smtpObj = smtplib.SMTP()
    smtpObj.connect(mail_host, 25)
    smtpObj.login(mail_user, mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("Email sent successfully")
except smtplib.SMTPException:
    print("Error: could not send email")

Sending Email with Attachments

Using MIMEText with base64 encoding (text and code files)

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header

mail_host = "smtp.qq.com"
mail_user = "[email protected]"
mail_pass = "your_authorization_code"

sender    = '[email protected]'
receivers = ['[email protected]']

message = MIMEMultipart()
message['From']    = Header("Sender Name", 'utf-8')
message['To']      = Header("Recipient Name", 'utf-8')
message['Subject'] = Header('Email with Attachments', 'utf-8')

# Email body
content_obj = MIMEText('Hi, please find the attachments.', 'plain', 'utf-8')
message.attach(content_obj)

# Attachment 1: a .txt file
att1 = MIMEText(open('t1.txt', 'rb').read(), 'base64', 'utf-8')
att1["Content-Type"]        = 'application/octet-stream'
att1["Content-Disposition"] = 'attachment; filename="t1.txt"'  # Name shown in the email
message.attach(att1)

# Attachment 2: a .py file
att2 = MIMEText(open('t2.py', 'rb').read(), 'base64', 'utf-8')
att2["Content-Type"]        = 'application/octet-stream'
att2["Content-Disposition"] = 'attachment; filename="t2.py"'
message.attach(att2)

try:
    smtpObj = smtplib.SMTP()
    smtpObj.connect(mail_host, 25)
    smtpObj.login(mail_user, mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("Email sent successfully")
except smtplib.SMTPException:
    print("Error: could not send email")

Using MIMEApplication for binary files (PDF, doc, xls, mp3, jpg, etc.)

MIMEApplication defaults to application/octet-stream, which tells the email client to infer the file type from the extension:

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.header import Header

mail_host = "smtp.qq.com"
mail_user = "[email protected]"
mail_pass = "your_authorization_code"

sender    = '[email protected]'
receivers = ['[email protected]']

message = MIMEMultipart()
message['From']    = Header("Sender Name", 'utf-8')
message['To']      = Header("Recipient Name", 'utf-8')
message['Subject'] = Header('Email with Binary Attachments', 'utf-8')

content_obj = MIMEText('Hi, please find the attachments.', 'plain', 'utf-8')
message.attach(content_obj)

# Each file is read as binary and attached with a filename header
for filename in ['t1.txt', 'bg.mp3', 't3.xls', 't4.doc', 't5.pdf', 'img.jpg']:
    part = MIMEApplication(open(filename, 'rb').read())
    part.add_header('Content-Disposition', 'attachment', filename=filename)
    message.attach(part)

try:
    smtpObj = smtplib.SMTP()
    smtpObj.connect(mail_host, 25)
    smtpObj.login(mail_user, mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("Email sent successfully")
except smtplib.SMTPException:
    print("Error: could not send email")
Last updated on