Send to Kindle via email - using browser automation

I often read web articles on the Kindle e-reader for less eye strain. In order to achieve that, I use the Send to Kindle browser extension, maintained by Amazon. In the past few years many of Kindle publishing services just came and go, so the official Send to Kindle was always the most stable one to use. However, one important feature is not supported, sending articles from mobile or tablet - by just sending the URL to an email address, not an article’s content attached, processable by the @free.kindle.com email addresses. Here, my script is presented, which could run on any machines using Python to check an email box with unread emails containing URLs and send the articles’ content to the Kindle e-reader.

Approaches

  • I’ve experimented with a few, supposedly more elegant approaches
    • Web page content parsing

So I decided to leverage the most stable parser for the purpose, the Amazon extension that I use day by day on a manual basis.

Send to Kindle via web browser automation - Specification

  • Read a Gmail account’s specific folder for unread emails
    • In the below script, Kindle IMAP label, created in Gmail
  • Parse the first http(s) URL in the email
  • Open up Chrome browser with the send to Kindle extension
  • Use the Send to Kindle extension to send the article content to the Kindle
  • Mark the messages processed as read

Prerequisites

  • Windows or Linux
  • Python 2.7
  • Chrome browser (Firefox was not working with Selenium and Send-To-Kindle extension
  • Selenium’s Chrome web driver installed
  • pip install selenium
  • Gmail rule setup, emails sent to mail+kindle@gmail.com should be labeled as Kindle (= put to Kindle IMAP folder)
    • Email messages sent to your_mail+@gmail.com will arrive to your inbox, but you can setup various filtering rules for managing such emails

Script

Richly commented Python 2.7 code, reading and processing emails

gmail_uname = '' #without @gmail.com

gmail_password = ''

imap_folder_of_mails = 'Kindle'

amz_uname = ''

amz_password = ''

chromepath = 'C:/Users/{user}/AppData/Local/Google/Chrome/User Data' #use / and don't add /Default at the end



import imaplib # For email parsing

import time # For waits

import sys

import re

from selenium import webdriver #For selenium

from selenium.webdriver import ActionChains

from selenium.webdriver.common.by import By

from selenium.webdriver.common.keys import Keys

from selenium.webdriver.chrome.options import Options





def read():

    # Login to Kindle Imap folder of gmail

    imap = imaplib.IMAP4_SSL("imap.gmail.com", 993)

    imap.login(gmail_uname, gmail_password)

    imap.select(imap_folder_of_mails)



    # Use search(), not status() to get unread messages

    status, response = imap.search(None, '(UNSEEN)')

    unread_msg_nums = response[0].split()

    

    # Get the content of the unread messages

    for e_id in unread_msg_nums:

        _, response = imap.fetch(e_id, '(UID BODY[TEXT])')

        emails.append(response[0][1])  

	

    # Mark emails as read - not necessary as parsed emails will be marked as read without this command

    # for e_id in unread_msg_nums:

    #    imap.store(e_id, '+FLAGS', 'Seen')



# Tuple hosting all the pages to be sent

emails = [] 

# Parse the emails for the URLs

read()

if emails:

    # Open the browser

    options = webdriver.ChromeOptions()

    options.add_argument('user-data-dir=' + chromepath) #Path to your chrome profile

    driver = webdriver.Chrome(chrome_options=options)

    actions = ActionChains(driver)



# For each of the websites

    for email in emails:

        try:

            # Get the first word of the email, that is the URL to be sent to Kindle

            words = re.findall(r'(https?://S+)', email)    

            url = words[0]

            print url

            driver.get(url)

    	# Wait 11 seconds - needed for Send to Kindle extension

            time.sleep(11)

            # Push SHIFT + K for send to Kindle extension to send the article

            actions.key_down(Keys.ALT).send_keys('k').key_up(Keys.ALT).perform()

          	# Wait for the article to be sent

            time.sleep(13)

       	# Login to Amazon, if needed

            try:

                # Check for Amazon logo - not a necessary step - if not found, jump to exception

                driver.find_element_by_xpath("//html/body/div[1]/div[1]/div[3]/div/div/form/div/div/div/h1") 

                # Send the UserID

                element = driver.find_element_by_xpath("//html/body/div[1]/div[1]/div[3]/div/div/form/div/div/div/div[1]/input")

                element.clear()

                element.send_keys(amz_uname)

                # Send the Amazon Password

                element = driver.find_element_by_xpath("//html/body/div[1]/div[1]/div[3]/div/div/form/div/div/div/div[2]/input")

                element.clear()

                element.send_keys(amz_password)

                # Click the login button

                element = driver.find_element_by_xpath("//html/body/div[1]/div[1]/div[3]/div/div/form/div/div/div/div[3]/span/span/input")

                element.click() 

                # Wait for parsing by Send to Kindle extension

                time.sleep(20)

            except:

                print "Already logged in / changed login screen", sys.exc_info()[0]        

        except:

            print "Unparseable URL in email", sys.exc_info()[0]

    driver.quit()