DOM_SMOOTHIE
A Rust crate for extracting readable content from web pages.
dom_smoothie closely follows the implementation of readability.js, bringing its functionality to Rust.
Examples
use Error;
use ;
use Error;
use ;
use Error;
use Document;
use Readability;
use Error;
use ;
Unfortunately, the approach used in mozilla/readability does not always produce the desired result when extracting meaningful content. Sometimes, this approach discards part of the content simply because there were fewer than three alternative candidates to the best one. While this method does a good job, it still relies on too many magic numbers.
After @emschwartz discovered this issue, I decided to add an alternative implementation for finding the common candidate. Currently, this implementation may produce a less "clean" result compared to mozilla/readability, but in return, it can capture more of the meaningful content, whereas the original approach from mozilla/readability may fail in some cases.
That said, this approach is not necessarily superior to the original—there is still room for improvement.
use Error;
use ;
By default, the text content is output as-is, without formatting, preserving whitespace from the original HTML document. Depending on the document's initial markup, this can be quite verbose and inconvenient.
To retrieve formatted text content, set text_mode: TextMode::Formatted in the config.
This formatting does not preserve table structures, meaning table data may be output as plain text without column alignment.
While this formatting is not as structured as Markdown, it provides a cleaner output compared to raw text.
TextMode::Markdown enables Markdown formatting.
use Error;
use ;
The Readability::parse_with_policy method allows parsing content with a specific policy.
This method follows the same steps as Readability::parse but makes only a single attempt using the specified ParsePolicy.
As a result, it doesn't store the best attempt, leading to significantly lower memory consumption. Some policies may also be faster than others.
Typically, ParsePolicy::Strict is the slowest but provides the cleanest result. ParsePolicy::Moderate can also yield a good result, while the others may be less accurate.
In some cases, using certain policies (e.g., ParsePolicy::Strict) may result in a ReadabilityError::GrabFailed error, whereas Readability::parse might succeed.
This happens because Readability::parse attempts parsing with different policies (essentially a set of grab flags) until it either succeeds or exhausts all options.
use Error;
use ;
Crate Features
serde: Enables theserde::Serializeandserde::Deserializetraits for theArticle,Metadata, andConfigstructures.aho-corasick: Enables the use of theaho-corasickcrate for defining unlikely candidates and for the node scoring process. This can speed up the parsing by 5-10% in some cases, at the cost of slightly higher memory usage and a larger binary size.
Differences from Mozilla/Readability.js
Absolute URL normalization
dom_smoothiedoes not modifyhrefattributes if they contain absolute URLs — they remain untouched.Readability.jsnormalizes URLs — it may add a trailing slash and normalize text case.
Example:
https://fetch.spec.whatwg.org // dom_smoothie
https://fetch.spec.whatwg.org/ // Readability.js
DOM simplification
dom_smoothiemore aggressively removes parent<div>elements when they contain only a single<p>or<div>element.
Attribute cleanup
dom_smoothieremoves allfontattributes and converts<font>elements to<span>.Readability.jsconverts<font>to<span>but preserves all attributes.
Empty links handling
dom_smoothieremoves<a>elements without anhrefattribute or without child nodes.Readability.jskeeps such elements.
Class preservation
- In
dom_smoothie,class="page"is preserved only for the article node (id="readability-page-1"), unless explicitly allowed viaConfig.classes_to_preserve. Readability.jspreserves it across the document.
Filtering order differences
Note: This behavior applies to versions ≤ 0.16.0.
In Readability.js, element filtering (removal of unwanted nodes) and scoring are performed in a single stage.
In dom_smoothie, part of the filtering is applied globally across all parsing attempts,
while another part is applied per attempt. This approach helps better eliminate "nested"
structures (e.g., <div> elements that contain only <div> or <p>).
However, there is a trade-off — the output may slightly differ from Readability.js in terms of which elements remain.
This mostly affects elements that overlap with UNLIKELY CANDIDATES.
In practice, this may result in duplicate headings (<h1>, <h2>) and byline elements.
This happens because unlikely candidates may include containers that wrap such elements.
For convenience, all known cases of these differences are collected here: test-pages/not-matching.
See Also
- readability-rs: a fork of the currently unmaintained readability crate.
Changelog
License
Licensed under MIT (LICENSE or http://opensource.org/licenses/MIT).
Contribution
Any contribution intentionally submitted for inclusion in this project will be licensed under the MIT license, without any additional terms or conditions.