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+@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()