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
- Readability libraries are now maintained by Mercury as an API at https://mercury.postlight.com/web-parser/ – it was not capable of handling special characters (ISO-8859-2 charset, with e.g. accents as ????)
- Used the Python-readability project, that errored out on some webpages
- 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
- Chrome browser having the Send to Kindle extension installed
- 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+[anytoken]@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()