Automate the Boring Stuff with Python

Home > Other > Automate the Boring Stuff with Python > Page 37
Automate the Boring Stuff with Python Page 37

by Al Sweigart


  For a long-term countdown, you can use timedelta objects to measure the number of days, hours, minutes, and seconds until some point (a birthday? an anniversary?) in the future.

  Summary

  The Unix epoch (January 1, 1970, at midnight, UTC) is a standard reference time for many programming languages, including Python. While the time.time() function module returns an epoch timestamp (that is, a float value of the number of seconds since the Unix epoch), the datetime module is better for performing date arithmetic and formatting or parsing strings with date information.

  The time.sleep() function will block (that is, not return) for a certain number of seconds. It can be used to add pauses to your program. But if you want to schedule your programs to start at a certain time, the instructions at http://nostarch.com/automatestuff/ can tell you how to use the scheduler already provided by your operating system.

  The threading module is used to create multiple threads, which is useful when you need to download multiple files or do other tasks simultaneously. But make sure the thread reads and writes only local variables, or you might run into concurrency issues.

  Finally, your Python programs can launch other applications with the subprocess.Popen() function. Command line arguments can be passed to the Popen() call to open specific documents with the application. Alternatively, you can use the start, open, or see program with Popen() to use your computer’s file associations to automatically figure out which application to use to open a document. By using the other applications on your computer, your Python programs can leverage their capabilities for your automation needs.

  Practice Questions

  Q:

  1. What is the Unix epoch?

  Q:

  2. What function returns the number of seconds since the Unix epoch?

  Q:

  3. How can you pause your program for exactly 5 seconds?

  Q:

  4. What does the round() function return?

  Q:

  5. What is the difference between a datetime object and a timedelta object?

  Q:

  6. Say you have a function named spam(). How can you call this function and run the code inside it in a separate thread?

  Q:

  7. What should you do to avoid concurrency issues with multiple threads?

  Q:

  8. How can you have your Python program run the calc.exe program located in the C:WindowsSystem32 folder?

  Practice Projects

  For practice, write programs that do the following.

  Prettified Stopwatch

  Expand the stopwatch project from this chapter so that it uses the rjust() and ljust() string methods to “prettify” the output. (These methods were covered in Chapter 6.) Instead of output such as this:

  Lap #1: 3.56 (3.56) Lap #2: 8.63 (5.07) Lap #3: 17.68 (9.05) Lap #4: 19.11 (1.43)

  ... the output will look like this:

  Lap # 1: 3.56 ( 3.56) Lap # 2: 8.63 ( 5.07) Lap # 3: 17.68 ( 9.05) Lap # 4: 19.11 ( 1.43)

  Note that you will need string versions of the lapNum, lapTime, and totalTime integer and float variables in order to call the string methods on them.

  Next, use the pyperclip module introduced in Chapter 6 to copy the text output to the clipboard so the user can quickly paste the output to a text file or email.

  Scheduled Web Comic Downloader

  Write a program that checks the websites of several web comics and automatically downloads the images if the comic was updated since the program’s last visit. Your operating system’s scheduler (Scheduled Tasks on Windows, launchd on OS X, and cron on Linux) can run your Python program once a day. The Python program itself can download the comic and then copy it to your desktop so that it is easy to find. This will free you from having to check the website yourself to see whether it has updated. (A list of web comics is available at http://nostarch.com/automatestuff/.)

  Chapter 16. Sending Email and Text Messages

  Checking and replying to email is a huge time sink. Of course, you can’t just write a program to handle all your email for you, since each message requires its own response. But you can still automate plenty of email-related tasks once you know how to write programs that can send and receive email.

  For example, maybe you have a spreadsheet full of customer records and want to send each customer a different form letter depending on their age and location details. Commercial software might not be able to do this for you; fortunately, you can write your own program to send these emails, saving yourself a lot of time copying and pasting form emails.

  You can also write programs to send emails and SMS texts to notify you of things even while you’re away from your computer. If you’re automating a task that takes a couple of hours to do, you don’t want to go back to your computer every few minutes to check on the program’s status. Instead, the program can just text your phone when it’s done—freeing you to focus on more important things while you’re away from your computer.

  SMTP

  Much like HTTP is the protocol used by computers to send web pages across the Internet, Simple Mail Transfer Protocol (SMTP) is the protocol used for sending email. SMTP dictates how email messages should be formatted, encrypted, and relayed between mail servers, and all the other details that your computer handles after you click Send. You don’t need to know these technical details, though, because Python’s smtplib module simplifies them into a few functions.

  SMTP just deals with sending emails to others. A different protocol, called IMAP, deals with retrieving emails sent to you and is described in IMAP.

  Sending Email

  You may be familiar with sending emails from Outlook or Thunderbird or through a website such as Gmail or Yahoo! Mail. Unfortunately, Python doesn’t offer you a nice graphical user interface like those services. Instead, you call functions to perform each major step of SMTP, as shown in the following interactive shell example.

  Note

  Don’t enter this example in IDLE; it won’t work because smtp.example.com, [email protected], MY_SECRET_PASSWORD, and [email protected] are just placeholders. This code is just an overview of the process of sending email with Python.

  >>> import smtplib >>> smtpObj = smtplib.SMTP('smtp.example.com', 587) >>> smtpObj.ehlo() (250, b'mx.example.com at your service, [216.172.148.131]nSIZE 35882577 n8BITMIMEnSTARTTLSnENHANCEDSTATUSCODESnCHUNKING') >>> smtpObj.starttls() (220, b'2.0.0 Ready to start TLS') >>> smtpObj.login('[email protected]', ' MY_SECRET_PASSWORD') (235, b'2.7.0 Accepted') >>> smtpObj.sendmail('[email protected]', '[email protected]', 'Subject: So long.nDear Alice, so long and thanks for all the fish. Sincerely, Bob') {} >>> smtpObj.quit() (221, b'2.0.0 closing connection ko10sm23097611pbd.52 - gsmtp')

  In the following sections, we’ll go through each step, replacing the placeholders with your information to connect and log in to an SMTP server, send an email, and disconnect from the server.

  Connecting to an SMTP Server

  If you’ve ever set up Thunderbird, Outlook, or another program to connect to your email account, you may be familiar with configuring the SMTP server and port. These settings will be different for each email provider, but a web search for smtp settings should turn up the server and port to use.

  The domain name for the SMTP server will usually be the name of your email provider’s domain name, with smtp. in front of it. For example, Gmail’s SMTP server is at smtp.gmail.com. Table 16-1 lists some common email providers and their SMTP servers. (The port is an integer value and will almost always be 587, which is used by the command encryption standard, TLS.)

  Table 16-1. Email Providers and Their SMTP Servers

  Provider

  SMTP server domain name

  Gmail

  smtp.gmail.com

  Outlook.com/Hotmail.com

  smtp-mail.outlook.com

  Yahoo Mail

  smtp.mail.yahoo.com

  AT&T

  smpt.mail.att.net (port 465)

  Comcast

>   smtp.comcast.net

  Verizon

  smtp.verizon.net (port 465)

  Once you have the domain name and port information for your email provider, create an SMTP object by calling smptlib.SMTP(), passing the domain name as a string argument, and passing the port as an integer argument. The SMTP object represents a connection to an SMTP mail server and has methods for sending emails. For example, the following call creates an SMTP object for connecting to Gmail:

  >>> smtpObj = smtplib.SMTP('smtp.gmail.com', 587) >>> type(smtpObj)

  Entering type(smtpObj) shows you that there’s an SMTP object stored in smtpObj. You’ll need this SMTP object in order to call the methods that log you in and send emails. If the smptlib.SMTP() call is not successful, your SMTP server might not support TLS on port 587. In this case, you will need to create an SMTP object using smtplib.SMTP_SSL() and port 465 instead.

  >>> smtpObj = smtplib.SMTP_SSL('smtp.gmail.com', 465)

  Note

  If you are not connected to the Internet, Python will raise a socket.gaierror: [Errno 11004] getaddrinfo failed or similar exception.

  For your programs, the differences between TLS and SSL aren’t important. You only need to know which encryption standard your SMTP server uses so you know how to connect to it. In all of the interactive shell examples that follow, the smtpObj variable will contain an SMTP object returned by the smtplib.SMTP() or smtplib.SMTP_SSL() function.

  Sending the SMTP “Hello” Message

  Once you have the SMTP object, call its oddly named ehlo() method to “say hello” to the SMTP email server. This greeting is the first step in SMTP and is important for establishing a connection to the server. You don’t need to know the specifics of these protocols. Just be sure to call the ehlo() method first thing after getting the SMTP object or else the later method calls will result in errors. The following is an example of an ehlo() call and its return value:

  >>> smtpObj.ehlo() (250, b'mx.google.com at your service, [216.172.148.131]nSIZE 35882577 n8BITMIMEnSTARTTLSnENHANCEDSTATUSCODESnCHUNKING')

  If the first item in the returned tuple is the integer 250 (the code for “success” in SMTP), then the greeting succeeded.

  Starting TLS Encryption

  If you are connecting to port 587 on the SMTP server (that is, you’re using TLS encryption), you’ll need to call the starttls() method next. This required step enables encryption for your connection. If you are connecting to port 465 (using SSL), then encryption is already set up, and you should skip this step.

  Here’s an example of the starttls() method call:

  >>> smtpObj.starttls() (220, b'2.0.0 Ready to start TLS')

  starttls() puts your SMTP connection in TLS mode. The 220 in the return value tells you that the server is ready.

  Logging in to the SMTP Server

  Once your encrypted connection to the SMTP server is set up, you can log in with your username (usually your email address) and email password by calling the login() method.

  >>> smtpObj.login(' [email protected] ', ' MY_SECRET_PASSWORD ') (235, b'2.7.0 Accepted')

  Gmail’s Application-Specific Passwords

  Gmail has an additional security feature for Google accounts called application-specific passwords. If you receive an Application-specific password required error message when your program tries to log in, you will have to set up one of these passwords for your Python script. Check out the resources at http://nostarch.com/automatestuff/ for detailed directions on how to set up an application-specific password for your Google account.

  Pass a string of your email address as the first argument and a string of your password as the second argument. The 235 in the return value means authentication was successful. Python will raise an smtplib.SMTPAuthenticationError exception for incorrect passwords.

  Warning

  Be careful about putting passwords in your source code. If anyone ever copies your program, they’ll have access to your email account! It’s a good idea to call input() and have the user type in the password. It may be inconvenient to have to enter a password each time you run your program, but this approach will prevent you from leaving your password in an unencrypted file on your computer where a hacker or laptop thief could easily get it.

  Sending an Email

  Once you are logged in to your email provider’s SMTP server, you can call the sendmail() method to actually send the email. The sendmail() method call looks like this:

  >>> smtpObj.sendmail(' [email protected] ', ' [email protected] ', 'Subject: So long.nDear Alice, so long and thanks for all the fish. Sincerely, Bob') {}

  The sendmail() method requires three arguments.

  Your email address as a string (for the email’s “from” address)

  The recipient’s email address as a string or a list of strings for multiple recipients (for the “to” address)

  The email body as a string

  The start of the email body string must begin with 'Subject: n' for the subject line of the email. The 'n' newline character separates the subject line from the main body of the email.

  The return value from sendmail() is a dictionary. There will be one key-value pair in the dictionary for each recipient for whom email delivery failed. An empty dictionary means all recipients were successfully sent the email.

  Disconnecting from the SMTP Server

  Be sure to call the quit() method when you are done sending emails. This will disconnect your program from the SMTP server.

  >>> smtpObj.quit() (221, b'2.0.0 closing connection ko10sm23097611pbd.52 - gsmtp')

  The 221 in the return value means the session is ending.

  To review all the steps for connecting and logging in to the server, sending email, and disconnection, see Sending Email.

  IMAP

  Just as SMTP is the protocol for sending email, the Internet Message Access Protocol (IMAP) specifies how to communicate with an email provider’s server to retrieve emails sent to your email address. Python comes with an imaplib module, but in fact the third-party imapclient module is easier to use. This chapter provides an introduction to using IMAPClient; the full documentation is at http://imapclient.readthedocs.org/.

  The imapclient module downloads emails from an IMAP server in a rather complicated format. Most likely, you’ll want to convert them from this format into simple string values. The pyzmail module does the hard job of parsing these email messages for you. You can find the complete documentation for PyzMail at http://www.magiksys.net/pyzmail/.

  Install imapclient and pyzmail from a Terminal window. Appendix A has steps on how to install third-party modules.

  Retrieving and Deleting Emails with IMAP

  Finding and retrieving an email in Python is a multistep process that requires both the imapclient and pyzmail third-party modules. Just to give you an overview, here’s a full example of logging in to an IMAP server, searching for emails, fetching them, and then extracting the text of the email messages from them.

  >>> import imapclient >>> imapObj = imapclient.IMAPClient('imap.gmail.com', ssl=True) >>> imapObj.login(' [email protected] ', ' MY_SECRET_PASSWORD ') '[email protected] Jane Doe authenticated (Success)' >>> imapObj.select_folder('INBOX', readonly=True) >>> UIDs = imapObj.search(['SINCE 05-Jul-2014']) >>> UIDs [40032, 40033, 40034, 40035, 40036, 40037, 40038, 40039, 40040, 40041] >>> rawMessages = imapObj.fetch([40041], ['BODY[]', 'FLAGS']) >>> import pyzmail >>> message = pyzmail.PyzMessage.factory(rawMessages[40041]['BODY[]']) >>> message.get_subject() 'Hello!' >>> message.get_addresses('from') [('Edward Snowden', '[email protected]')] >>> message.get_addresses('to') [(Jane Doe', '[email protected]')] >>> message.get_addresses('cc') [] >>> message.get_addresses('bcc') [] >>> message.text_part != None True >>> message.text_part.get_payload().decode(message.text_part.charset) 'Follow the money.rnrn-Edrn' >>> message.html_part != None True >>> message.html_part.get_payload().decode(message.html_part.charset) '

So long, and thanks for all
the fish!

- Al
rn' >>> imapObj.logout()

  You don’t have to memorize these steps. After we go through each step in detail, you can come back to this overview to refresh your memory.

  Connecting to an IMAP Server

  Just like you needed an SMTP object to connect to an SMTP server and send email, you need an IMAPClient object to connect to an IMAP server and receive email. First you’ll need the domain name of your email provider’s IMAP server. This will be different from the SMTP server’s domain name. Table 16-2 lists the IMAP servers for several popular email providers.

  Table 16-2. Email Providers and Their IMAP Servers

  Provider

  IMAP server domain name

 

‹ Prev