Readability
A web page's article without its menus, ads and footer: title, author, date, clean HTML, plain text or Markdown, for read-later apps, link previews and summaries.
Version 1.0.0 · 3 functions · 1 crate
Functions
What use readability.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use readability_extract, readability_markdown from readability (Quillo 0.6).
readability_extract(html: str, url: str) → anyor an errorThe article in a page's html: { title, byline, excerpt, site_name, lang, published_time, image, html, text, length }. url is the page's address (or ""), so relative links in the article become whole ones. text keeps paragraphs apart; length counts its characters. An error when no article is found.
readability_markdown(html: str, url: str) → stror an errorThe article's main text as Markdown: headings, lists, links and emphasis kept, for an LLM or a plain-text view.
readability_probable(html: str) → boolWhether a page looks like it holds an article worth extracting (enough paragraphs of text), cheaply.
Get it
$ quillo install readability # readability.qwer into this folder$ quillo install readability --example # and its example, readability.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/readability.qwer$ curl -O https://quillolang.org/plugins/files/readability.qwe$ curl -O https://quillolang.org/plugins/files/readability_sample.htmlThen, in your program, all of it or only what you call:
use readability.qweruse readability_extract, readability_markdown from readability // Quillo 0.6: only theseIt uses this crate, which cargo fetches the first time the plugin is built:
dom_smoothie = "0.19"Example
// A read-later view: a saved page's article, without its menus, ads and footer.//// quillo run readability.qwe then open http://localhost:8080/articleuse "readability.qwer"let page = read_file("readability_sample.html")?route GET /article { let a = readability_extract(page, "https://blog.example.com/posts/faster-builds")? { title: a.title, by: a.byline, site: a.site_name, published: a.published_time, image: a.image, words: len(split(a.text, " ")), text: a.text }}route GET /read { let a = readability_extract(page, "https://blog.example.com/posts/faster-builds")? <html> <body> <h1>{a.title}</h1> <p><i>{a.byline}</i></p> {raw(a.html)} </body> </html>}route GET /markdown = respond(200, readability_markdown(page, "https://blog.example.com/posts/faster-builds")?, { "content-type": "text/markdown; charset=utf-8" })route GET /probable = { article: readability_probable(page), menu: readability_probable("<nav><a href='/'>Home</a></nav>") }$ quillo run readability.qwe$ curl 'localhost:8080/article'$ curl 'localhost:8080/markdown'$ curl 'localhost:8080/probable'Source
//! A web page's article without its menus, ads and footers: title, author, date, main text and clean HTML, for read-later apps, previews and summaries.//! version: 1.0.0//!//! [dependencies]//! dom_smoothie = "0.19"use dom_smoothie::{Config, Readability, TextMode};use serde_json::{json, Value};const MAX: usize = 5_000_000;fn parse(html: &str, url: &str, mode: TextMode) -> Result<dom_smoothie::Article, String> { if html.len() > MAX { return Err("pages up to 5 MB".into()); } let cfg = Config { text_mode: mode, max_elements_to_parse: 100_000, ..Default::default() }; let url = if url.trim().is_empty() { None } else { Some(url.trim()) }; let mut r = Readability::new(html, url, Some(cfg)).map_err(|e| e.to_string())?; r.parse().map_err(|e| e.to_string())}/// The article in a page's `html`: `{ title, byline, excerpt, site_name, lang, published_time, image, html, text, length }`. `url` is the page's address (or ""), so relative links in the article become whole ones. `text` keeps paragraphs apart; `length` counts its characters. An error when no article is found.pub fn readability_extract(html: &str, url: &str) -> Result<Value, String> { let a = parse(html, url, TextMode::Formatted)?; Ok(json!({ "title": a.title, "byline": a.byline, "excerpt": a.excerpt, "site_name": a.site_name, "lang": a.lang, "published_time": a.published_time, "image": a.image, "html": a.content.to_string(), "text": a.text_content.trim().to_string(), "length": a.length, }))}/// The article's main text as Markdown: headings, lists, links and emphasis kept, for an LLM or a plain-text view.pub fn readability_markdown(html: &str, url: &str) -> Result<String, String> { Ok(parse(html, url, TextMode::Markdown)?.text_content.trim().to_string())}/// Whether a page looks like it holds an article worth extracting (enough paragraphs of text), cheaply.pub fn readability_probable(html: &str) -> bool { html.len() <= MAX && Readability::new(html, None, None).map(|r| r.is_probably_readable()).unwrap_or(false)}Related
html_markdown.qwer · 1.0.0HTML turned into clean Markdown as Turndown does: headings, lists, links, images, code blocks, quotes and tables kept, scripts and the parts you name dropped; for importing old posts, editor output, and pages for an LLM.7 views · 6 downloadsHTML to text html_text.qwer · 1.0.0HTML as readable plain text for the text part of an email: headings, lists and tables laid out, links listed at the end, and one-line previews.7 views · 7 downloadsScrape pages scrape.qwer · 1.0.0CSS selectors over HTML, a page's link preview (title, description, image) and its text without scripts.6 views · 7 downloadsSyntax highlighting highlight.qwer · 1.0.0Code highlighted as HTML in 75 languages, with inline colours from a theme or with classes and a stylesheet, for docs and blog posts.8 views · 6 downloadsTemplates templates.qwer · 1.0.0Jinja templates your users can edit for emails, invoices and pages: sandboxed, escaped for HTML, with filters such as money and truncate, and checked before they are saved.7 views · 8 downloadsTypography typography.qwer · 1.0.0Typed text set as a typographer would, by language: curly quotes (“ ”, « », „ “) and apostrophes, en and em dashes, ellipses, ×, ©, French spaces before ; : ! ?, and no widows; in plain text or HTML, code left alone.7 views · 10 downloadsThread 0 posts
On Readability: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.