https://blog.owulveryck.info/2021/10/07/reading-from-the-web-offline-and-distraction-free.html owulveryck's blog * This is Home * Archives * Tags * Categories * About owulveryck's blog * This is Home * Archives * Tags * Categories * About Reading from the web offline and distraction-free 2021-10-07 Table of Contents * The Why + This article * Existent solutions * First part: extracting the content + Readability / Arc90 + The problem with reactive content and Medium articles * Second part: generating the ePub + First step: crafting the ePub + Second step: creating the ePub * Third part: adding fancy features + Grabbing meta information + Generating a cover + GetPocket integration + Dealing with MathJax * Conclusion and future work TL;DR: This article describes the wiring of a tool to turn a webpage into a self-sufficient epub (for reading offline). If you want to try the tool, you can grab a binary version from GitHub The Why To oversimplify my need, I will quote this from the Readability Project Reading anything on the Internet has become a full-on nightmare. As media outlets attempt to eke out as much advertising revenue as possible, we're left trying to put blinders on to mask away all the insanity that surrounds the content we're trying to read. It's almost like listening to talk radio, except the commercials play during the program in the background. It's a pretty awful experience. Our friend to date has been the trusty "Print View" button. Click it and all the junk goes away. I click it all the time and rarely print. It's really become the "Peace & Quiet" button for many. This article In a recent post, I blogged about a tool I am building for my reMarkable. In this post, I will describe a new tool that converts any webpage into an ePub file. The goals of this tool are: * to keep track of the articles I like without fearing any broken links * to extract the content, and read the articles without distraction * to be able to read the articles offline on devices such as ebook readers or my reMarkable Existent solutions This feature already exists if you are using a Kobo and the getPocket service. The problem is that it is that the offline experience is tidily linked with my Kobo device. On top of that, getPocket does not offer any way to download the cleaned version of the articles. We, as developers, have superpowers: we can build the tools we want. Let's explain the features I am building step by step. Disclaimer at the time this post is written, the tool results from various experiments, but not the architecture or the code is clean and maintainable. Take this post as a validation of a proof of concept. First part: extracting the content The most important part of this journey is the tool's ability to extract the content of a webpage. The first idea would be to query the getPocket service that does this, but the documentation of their API mentions that: Pocket's Article View API will return article content and relevant meta data on any provided URL. The Pocket Article View API is currently only open to partners that are integrating Pocket specific features or full-fledged Pocket clients. For example, building a Pocket client for X platform. If you are looking for a general text parser or to provide "read now" functionality in your app - we do not currently support that. There are other companies/products that provide that type of API, for example: Diffbot. They mention Diffbot, but it is a web service that requires a subscription; I'd like to build a simple tool, free of charge, for my usage, and therefore this is not an option. Readability / Arc90 I looked into open source initiatives that empower the reading modes of the browsers (I am/was a fan of the safari reading mode), and I found that some of them were based on an experiment made by Arc90. This experiment led to the (discontinued) service readability. We can now find various implementations of the Arc90 algorithm. I am using this implementation in Go for my tool. Code Feel free to skip this part if you are not interested in the code The API of the readability library is straightforward. First, there is a need to create a Readability object with an HTML parser that reads and extracts relevant content. Then, calling the Parse method on this object, feeding it with an io.Reader that contains the page to analyze. The result is an object of type Article that contains some metadata and the cleaned content. This content is an HTML tree and is accessible via a top-level html.Node. 1 package main 2 3 import ( 4 "log" 5 "net/http" 6 "os" 7 8 "github.com/cixtor/readability" 9 "golang.org/x/net/html" 10 ) 11 12 func main() { 13 // create a parser 14 htmlParser := readability.New() 15 // Fetch a webpage 16 resp, err := http.Get("https://example.com/") 17 passOrDie(err) 18 // Deal with errors etc... 19 defer resp.Body.Close() 20 // Parse the content 21 article, err := htmlParser.Parse(resp.Body, "https://example.com") 22 passOrDie(err) 23 // Write the readable result on stdout 24 html.Render(os.Stdout, article.Node) 25 } The problem with reactive content and Medium articles When the Arc90 project made this experiment, there were not many reactive contents. On top of that, it does not handle the javascript. This leads to images that are not correctly displayed. Let's take the first chapter of Simon Wardley's book about maps to illustrate the problem. The picture below is a screenshot of a reader view of the page with Safari: [medium] The medium issue with Arc90 The code below is the code extracter by a curl request: 1
3
5
6
7
9 18 31
32
33
34
35
Within the
element, we can see that the first image (https:/ /miro.medium.com/max/60/1*RSH2vh_xgQtjB68Zb7oBaA.jpeg?q=20) is a thumbnail and it acts as a placeholder. A couple of JavaScript routines replaces the image at rendering time in the browser. Luckily a