I used Python to make a mailbox bomber to send e-mail to the finance. Unexpectedly, she chased me and shouted six

A project in recent work has the requirement to automatically send some information emails to the specified mailbox, so how to use Python to realize the function of automatically sending emails? Next, let's simply say.


Send mail using Python SMTP


SMTP (Simple Mail Transfer Protocol) is the simple mail transfer protocol. In other words, it is the protocol for sending mail. python's smplib library simply encapsulates the SMTP protocol and provides support for SMTP. It can send plain text mail, HTML files and mail with attachments.
First, we build a SendEmailManager class, which follows the idea of object-oriented programming. The general structure is as follows:

class SendEmailManager(object):

    def __init__(self, **kwargs):
        # Initialization parameters
        ...

    def _get_conf(self, key):
        # Get configuration parameters
        ...

    def _init_conf(self):
        # Initialize configuration parameters
        ...

    def _login_email(self):
        # Log in to the mailbox server
        ...
    def _make_mail_msg(self):
        # Building text mail objects
        ...

    def do_send_mail(self):
        # Mail sending
        ...

def __init__(self, **kwargs)

Class can be used to set object properties and give initial values, which can be parameters or fixed values. The parameter * * kwargs is to pass the dictionary of a variable keyword parameter to the function argument. Here, we mainly use the SMTP server (qq mailbox is used here), the proxy mailbox for sending mail, the client authorization password set in the mailbox Some initialization with variable parameters. The specific codes are as follows:


 

# SMTP server, qq mailbox is used here, and other mailboxes Baidu by themselves
EMAIL_HOST = 'smtp.qq.com'
# Proxy mailbox for sending mail
EMAIL_HOST_USER = 'xxxx@xxxx.com'
# The client authorization password set in the mailbox. Note that this is not the mailbox password. For how to obtain the mailbox authorization code, please Baidu yourself~~~
EMAIL_HOST_PASSWORD = 'xxxxxxxxxxxxx'
def __init__(self, **kwargs):
    # Initialization parameters
    self.email_host = EMAIL_HOST
    self.email_host_user = EMAIL_HOST_USER
    self.email_host_pass = EMAIL_HOST_PASSWORD
    self.kwargs = kwargs

def _get_conf(self, key)

It is mainly responsible for reading the value in the variable parameter self.kwargs dictionary through the key for use by other functions.

def _get_conf(self, key):
    # Get configuration parameters
    value = self.kwargs.get(key)
    if key != "attach_file_list" and (value is None or value == ''):
        raise Exception("configuration parameter '%s' cannot be empty" % key)
    return value

def _init_conf(self)

This function is mainly responsible for initializing the function_ get_ The configuration parameters returned by conf so that the next function can call the relevant configuration parameters.

def _init_conf(self):
    # Initialize configuration parameters
    print(self._get_conf('receives'))
    self.receives = self._get_conf('receives')
    self.msg_subject = self._get_conf('msg_subject')
    self.msg_content = self._get_conf('msg_content')
    self.msg_from = self._get_conf('msg_from')
    # attachment
    self.attach_file_list = self._get_conf('attach_file_list')

def _login_email(self)

Log in to the mail server. I log in to the qq mailbox server with the port number of 465. Please Baidu for other mailbox port numbers. The code is as follows:

def _login_email(self):
    # Log in to the mailbox server
    try:
        server = smtplib.SMTP_SSL(self.email_host, port=465)
        # set_ Debug level (1) can print out all information interacting with SMTP server
        server.set_debuglevel(1)
        # Login mailbox
        server.login(self.email_host_user, self.email_host_pass)
        return server
    except Exception as e:
        print("mail login exception:", e)
        raise e

def _make_mail_msg(self)

The function is to build a mail instance object to process the contents of the mail. A normal e-mail generally has sender and receiver information, e-mail subject and e-mail body. Some e-mails are also attached with attachments. See the following codes for specific settings:

def _make_mail_msg(self):
    # Building mail objects
    msg = MIMEMultipart()
    msg.attach(MIMEText(self.msg_content, 'plain', 'utf-8'))
    # Mail subject
    msg['Subject'] = Header(self.msg_subject, "utf-8")
    # Sender mailbox information
    msg['From'] = "<%s>" % self.msg_from
    # msg['From'] = Header(self.msg_from + "<%s>" % self.email_host_user, "utf-8")
    msg['To'] = ",".join(self.receives)
    print("---", self.attach_file_list)
    if self.attach_file_list:
        for i, att in enumerate(self.attach_file_list):
            # Construct attachments and transfer files in the current directory
            if not att:
                break
            att_i = MIMEText(open(att, 'rb').read(), 'base64', 'utf-8')
            att_i["Content-Type"] = 'application/octet-stream'
            # The filename here can be written arbitrarily. What name is written and what name is displayed in the email
            att_i["Content-Disposition"] = 'attachment; filename="%s"' % att
            msg.attach(att_i)
    return msg

def do_send_mail(self)

Sending e-mail is to string the above functions and directly write the code:

def do_send_mail(self):
    # Mail sending
    try:
        self._init_conf()
        server = self._login_email()
        msg = self._make_mail_msg()
        server.sendmail(self.email_host_user, self.receives, msg.as_string())
        server.close()
        print("Sending succeeded!")
    except Exception as e:
        print("Mail sending exception", e)

Configure parameters to test whether mail can be sent normally:

if __name__ == "__main__":
    mail_conf = {
        'msg_from': '****@foxmail.com',  # Mail sender's address
        'receives': ['****@qq.com',],  # The address of the mail recipient. This is a list because there may be more than one mail recipient
        'msg_subject': 'Python Send mail test',  # Subject of the message
        'msg_content': 'hello',  # Content of the message
        'attach_file_list': {"test.py": "test.py", "test.txt": "./test.txt"},  # It is a list of attachment file paths, or it may not be available
    }

    manager = SendEmailManager(**mail_conf)
    manager.do_send_mail()

ok, it's ok to send successfully and add attachments. Now that we have realized sending mail using Python, how to automatically send mail? Next, let's talk about it.

Send mail automatically

1. Code hard coding implementation

It is to loop in the code, add an if judgment, and send every 1 hour. The code is as follows:

def delayed_sending(manager):
    # The parameter manager is the SendEmailManager object
    begin_time = int(time.time())
    while True:
        end_time = int(time.time())
        if end_time - begin_time == 1*60*60:
            threading.Thread(target=manager.do_send_mail()).start()
            begin_time = end_time
            print("Sent...")

 

At the same time, in order to prevent blocking the normal operation of the program for unknown reasons, multithreading is added to realize asynchronous mail sending. In this way, the program will send an email to the specified mailbox every hour after execution. As long as the program does not stop, the mail will continue to be sent!!

Suppose we need to send an email to the designated mailbox at 8 o'clock every day, you can do this:

def delayed_sending(manager):
    # The parameter manager is the SendEmailManager object
    hour = 8
    minute = 0
    second = 0
    while True:
    now = datetime.datetime.now()
    if now.hour == hour and now.minute == minute and now.second == second:
        threading.Thread(target=manager.do_send_mail()).start()
        print("Sent...")

Although the functions of the above two methods have been realized, the implementation method is really low!

2. The timing function is realized through the module

In addition to hard coding, we can also implement it through the schedule module of task timing runtime in python:

# schedule module using python task timing runtime
def send_mail_by_schedule(manager):
    schedule.every(20).minutes.do(manager.do_send_mail)  # Every 20 minutes
    schedule.every().hour.do(manager.do_send_mail)  # Every hour
    schedule.every().day.at("10:00").do(manager.do_send_mail)  # Once every day at 10:00
    schedule.every().monday.do(manager.do_send_mail)  # Once a week on Monday
    schedule.every().friday.at("21:00").do(manager.do_send_mail)  # Every Friday at 21:00

    while True:
        schedule.run_pending()
        time.sleep(1)

 

Schedule is actually just a timer. In the while True loop, schedule.run_pending() is to keep the schedule running all the time to query the above pile of tasks. In the tasks, you can set different times to run. This is similar to crontab.

However, if multiple scheduled tasks are running, they are actually executed one by one from top to bottom in order. If the above tasks are complex and time-consuming, the running time of the following tasks will be affected. If you run 5 tasks every 2 minutes, each task takes 1 minute, which is a total of 5 minutes. In this way, when the next 2 minutes arrive, the task of the previous round is still running, and then a new round of tasks is started. The solution is also simple: use multithreading / multiprocessing.


 

def run_threaded(manager):
    threading.Thread(target=manager.do_send_mail()).start()


# schedule module using python task timing runtime
def send_mail_by_schedule(manager):
    schedule.every(1).minutes.do(run_threaded, manager)  # Every 20 minutes
    schedule.every().hour.do(run_threaded, manager)  # Every hour
    schedule.every().day.at("10:00").do(run_threaded, manager)  # Once every day at 10:00
    schedule.every().monday.do(run_threaded, manager)  # Once a week on Monday
    schedule.every().friday.at("21:00").do(run_threaded, manager)  # Every Friday at 21:00

    while True:
        schedule.run_pending()
        time.sleep(1)

In this way, we can create a thread for each scheduled task and let the task run in the thread, so as to achieve the effect of multiple tasks working in parallel. Is this method more time-saving and labor-saving than the first one, and it doesn't need a lot of code at all. It's perfect!

summary

In fact, to put it bluntly, the most important thing to automatically send e-mail is to realize the automatic execution of tasks, so there are many implementation schemes. For example, you can also use asynchronous task scheduler like celery to listen to the queue to realize the automatic execution of tasks, etc.

After completing the automatic e-mail, we can also let the program send us weather forecast, daily news, chicken soup and so on every day (you can also send it to boyfriend and girlfriend) hahaha.


 

Tags: Python Database Back-end Cache

Posted on Tue, 16 Nov 2021 06:12:21 -0500 by Wildthrust