README.md in html2rss-0.9.0 vs README.md in html2rss-0.10.0
- old
+ new
@@ -1,41 +1,54 @@
-![html2rss logo](https://github.com/gildesmarais/html2rss/raw/master/support/logo.png)
+![html2rss logo](https://github.com/html2rss/html2rss/raw/master/support/logo.png)
-[![Build Status](https://travis-ci.org/gildesmarais/html2rss.svg?branch=master)](https://travis-ci.org/gildesmarais/html2rss)
-[![Gem Version](https://badge.fury.io/rb/html2rss.svg)](http://rubygems.org/gems/html2rss/)
-[![Coverage Status](https://coveralls.io/repos/github/gildesmarais/html2rss/badge.svg?branch=master)](https://coveralls.io/github/gildesmarais/html2rss?branch=master)
-[![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/gems/html2rss)
-![Retro Badge: valid RSS](https://validator.w3.org/feed/images/valid-rss-rogers.png)
-[![](http://img.shields.io/liberapay/goal/gildesmarais.svg?logo=liberapa)](https://liberapay.com/gildesmarais/donate)
+[![Gem Version](https://badge.fury.io/rb/html2rss.svg)](http://rubygems.org/gems/html2rss/) [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/gems/html2rss) ![Retro Badge: valid RSS](https://validator.w3.org/feed/images/valid-rss-rogers.png) [![](http://img.shields.io/liberapay/goal/gildesmarais.svg?logo=liberapa)](https://liberapay.com/gildesmarais/donate)
-**Searching for a ready to use app which serves generated feeds via HTTP?**
-[Head over to `html2rss-web`!](https://github.com/gildesmarais/html2rss-web)
+`html2rss` is a Ruby gem that generates RSS 2.0 feeds from a _feed config_.
-This Ruby gem builds RSS 2.0 feeds from a _feed config_.
+With the _feed config_, you provide a URL to scrape and CSS selectors for extracting information (like title, URL, etc.). The gem builds the RSS feed accordingly. [Extractors](#using-extractors) and chainable [post processors](#using-post-processors) make information extraction, processing, and sanitizing a breeze. The gem also supports [scraping JSON](#scraping-and-handling-json-responses) responses and [setting HTTP request headers](#set-any-http-header-in-the-request).
-With the _feed config_ containing the URL to scrape and
-CSS selectors for information extraction (like title, URL, ...) your RSS builds.
-[Extractors](#using-extractors) and chain-able [post processors](#using-post-processors)
-make information extraction, processing and sanitizing a breeze.
-[Scraping JSON](#scraping-and-handling-json-responses) responses and
-[setting HTTP request headers](#set-any-http-header-in-the-request) is
-supported, too.
+**Looking for a ready-to-use app to serve generated feeds via HTTP?** [Check out `html2rss-web`](https://github.com/html2rss/html2rss-web)!
+Support the development by sponsoring this project on GitHub. Thank you! 💓
+
## Installation
-| 🤩 Like it? | Star it! ⭐️ |
-| ---------------------------------------------: | -------------------- |
-| Add this line to your application's `Gemfile`: | `gem 'html2rss'` |
-| Then execute: | `bundle` |
-| In your code: | `require 'html2rss'` |
+| Install | `gem install html2rss` |
+| ------- | ---------------------- |
+| Usage | `html2rss help` |
-😍 Love it? Feel free [to donate](https://liberapay.com/gildesmarais/donate). Thank you! 💓
+You can also install it as a dependency in your Ruby project:
-## Building a feed config
+| 🤩 Like it? | Star it! ⭐️ |
+| -------------------------------: | -------------------- |
+| Add this line to your `Gemfile`: | `gem 'html2rss'` |
+| Then execute: | `bundle` |
+| In your code: | `require 'html2rss'` |
-Here's a minimal working example:
+## Generating a feed on the CLI
+Create a file called `my_config_file.yml` with this example content:
+
+```yml
+channel:
+ url: https://stackoverflow.com/questions
+selectors:
+ items:
+ selector: "#hot-network-questions > ul > li"
+ title:
+ selector: a
+ link:
+ selector: a
+ extractor: href
+```
+
+Build the RSS with: `html2rss feed ./my_config_file.yml`.
+
+## Generating a feed with Ruby
+
+Here's a minimal working example in Ruby:
+
```ruby
require 'html2rss'
rss =
Html2rss.feed(
@@ -48,121 +61,130 @@
)
puts rss
```
-A _feed config_ consists of a `channel` and a `selectors` Hash.
-The contents of both hashes are explained below.
+## The _feed config_ and its options
-**Looks too complicated?** See [`html2rss-configs`](https://github.com/gildesmarais/html2rss-configs) for ready-made feed configs!
+A _feed config_ consists of a `channel` and a `selectors` hash. The contents of both hashes are explained below.
+Good to know:
+
+- You'll find extensive example feed configs at [`spec/*.test.yml`](https://github.com/html2rss/html2rss/tree/master/spec).
+- See [`html2rss-configs`](https://github.com/html2rss/html2rss-configs) for ready-made feed configs!
+- If you've created feed configs, you're invited to send a PR to [`html2rss-configs`](https://github.com/html2rss/html2rss-configs) to make your config available to the public.
+
+Alright, let's move on.
+
### The `channel`
-| attribute | | type | default | remark |
-| ------------- | -------- | ------- | -------------: | ------------------------------------------ |
-| `url` | required | String | | |
-| `title` | optional | String | auto-generated | |
-| `description` | optional | String | auto-generated | |
-| `ttl` | optional | Integer | `360` | TTL in _minutes_ |
-| `time_zone` | optional | String | `'UTC'` | TimeZone name |
-| `language` | optional | String | `'en'` | Language code |
-| `author` | optional | String | | Format: `email (Name)'` |
-| `headers` | optional | Hash | `{}` | Set HTTP request headers. See notes below. |
-| `json` | optional | Boolean | `false` | Handle JSON response. See notes below. |
+| attribute | | type | default | remark |
+| ------------- | ------------ | ------- | -------------- | ------------------------------------------ |
+| `url` | **required** | String | | |
+| `title` | optional | String | auto-generated | |
+| `description` | optional | String | auto-generated | |
+| `ttl` | optional | Integer | `360` | TTL in _minutes_ |
+| `time_zone` | optional | String | `'UTC'` | TimeZone name |
+| `language` | optional | String | `'en'` | Language code |
+| `author` | optional | String | | Format: `email (Name)` |
+| `headers` | optional | Hash | `{}` | Set HTTP request headers. See notes below. |
+| `json` | optional | Boolean | `false` | Handle JSON response. See notes below. |
+#### Dynamic parameters in `channel` attributes
+
+Sometimes there are structurally similar pages with different URLs. In such cases, you can add _dynamic parameters_ to the channel's attributes.
+
+Example of a dynamic `id` parameter in the channel URLs:
+
+```yml
+channel:
+ url: "http://domainname.tld/whatever/%<id>s.html"
+```
+
+Command line usage example:
+
+```sh
+bundle exec html2rss feed the_feed_config.yml id=42
+```
+
+<details><summary>See a Ruby example</summary>
+
+```ruby
+config = Html2rss::Config.new({ channel: { url: 'http://domainname.tld/whatever/%<id>s.html' } }, {}, { id: 42 })
+Html2rss.feed(config)
+```
+
+</details>
+
+See the more complex formatting options of the [`sprintf` method](https://ruby-doc.org/core/Kernel.html#method-i-sprintf).
+
### The `selectors`
-You must provide an `items` selector hash which contains the CSS selector.
-`items` needs to return a collection of HTML tags.
-The other selectors are scoped to the tags of the items' collection.
+First, you must give an **`items`** selector hash, which contains a CSS selector. The selector selects a collection of HTML tags from which the RSS feed items are built. Except for the `items` selector, all other keys are scoped to each item of the collection.
-To build a
-[valid RSS 2.0 item](http://www.rssboard.org/rss-profile#element-channel-item)
-each item has to have at least a `title` or a `description`.
+To build a [valid RSS 2.0 item](http://www.rssboard.org/rss-profile#element-channel-item), you need at least a `title` **or** a `description`. You can have both.
-Your `selectors` can contain arbitrary selector names, but only these
-will make it into the RSS feed:
+Having an `items` and a `title` selector is enough to build a simple feed.
-| RSS 2.0 tag | name in `html2rss` | remark |
-| ------------- | ------------------ | --------------------------- |
-| `title` | `title` | |
-| `description` | `description` | Supports HTML. |
-| `link` | `link` | A URL. |
-| `author` | `author` | |
-| `category` | `categories` | See notes below. |
-| `enclosure` | `enclosure` | See notes below. |
-| `pubDate` | `update` | An instance of `Time`. |
-| `guid` | `guid` | Generated from the `title`. |
-| `comments` | `comments` | A URL. |
-| `source` | ~~source~~ | Not yet supported. |
+Your `selectors` hash can contain arbitrary named selectors, but only a few will make it into the RSS feed (due to the RSS 2.0 specification):
+| RSS 2.0 tag | name in `html2rss` | remark |
+| ------------- | ------------------ | ------------------------------------------- |
+| `title` | `title` | |
+| `description` | `description` | Supports HTML. |
+| `link` | `link` | A URL. |
+| `author` | `author` | |
+| `category` | `categories` | See notes below. |
+| `guid` | `guid` | Default title/description. See notes below. |
+| `enclosure` | `enclosure` | See notes below. |
+| `pubDate` | `updated` | An instance of `Time`. |
+| `comments` | `comments` | A URL. |
+| `source` | ~~source~~ | Not yet supported. |
+
### The `selector` hash
-Your selector hash can have these attributes:
+Every named selector in your `selectors` hash can have these attributes:
| name | value |
| -------------- | -------------------------------------------------------- |
| `selector` | The CSS selector to select the tag with the information. |
| `extractor` | Name of the extractor. See notes below. |
| `post_process` | A hash or array of hashes. See notes below. |
-#### Reverse ordering of items
-
-The `items` selector hash can have an `order` attribute.
-If the value is `reverse` the order of items in the RSS will be reversed.
-
-<details>
- <summary>See a YAML feed config example</summary>
-
-```yml
-channel:
- # ... omitted
-selectors:
- items:
- selector: 'ul > li'
- order: 'reverse'
- # ... omitted
-```
-
-</details>
-
## Using extractors
Extractors help with extracting the information from the selected HTML tag.
- The default extractor is `text`, which returns the tag's inner text.
- The `html` extractor returns the tag's outer HTML.
- The `href` extractor returns a URL from the tag's `href` attribute and corrects relative ones to absolute ones.
- The `attribute` extractor returns the value of that tag's attribute.
- The `static` extractor returns the configured static value (it doesn't extract anything).
-- [See file list of extractors](https://github.com/gildesmarais/html2rss/tree/master/lib/html2rss/item_extractors).
+- [See file list of extractors](https://github.com/html2rss/html2rss/tree/master/lib/html2rss/item_extractors).
-Extractors can require additional attributes on the selector hash.
-👉 [Read their docs for usage examples](https://www.rubydoc.info/gems/html2rss/Html2rss/ItemExtractors).
+Extractors might need extra attributes on the selector hash. 👉 [Read their docs for usage examples](https://www.rubydoc.info/gems/html2rss/Html2rss/ItemExtractors).
-<details>
- <summary>See a Ruby example</summary>
+<details><summary>See a Ruby example</summary>
```ruby
Html2rss.feed(
channel: {}, selectors: { link: { selector: 'a', extractor: 'href' } }
)
```
</details>
-<details>
- <summary>See a YAML feed config example</summary>
+<details><summary>See a YAML feed config example</summary>
```yml
channel:
- # ... omitted
+ # ... omitted
selectors:
- # ... omitted
+ # ... omitted
link:
- selector: 'a'
- extractor: 'href'
+ selector: "a"
+ extractor: "href"
```
</details>
## Using post processors
@@ -180,52 +202,15 @@
| `substring` | Cuts a part off of a String, starting at a position. |
| `template` | Based on a template, it creates a new String filled with other selectors values. |
⚠️ Always make use of the `sanitize_html` post processor for HTML content. _Never trust the internet!_ ⚠️
-- [See file list of post processors](https://github.com/gildesmarais/html2rss/tree/master/lib/html2rss/attribute_post_processors).
-
-👉 [Read their docs for usage examples.](https://www.rubydoc.info/gems/html2rss/Html2rss/AttributePostProcessors)
-
-<details>
- <summary>See a Ruby example</summary>
-
-```ruby
-Html2rss.feed(
- channel: {},
- selectors: {
- description: {
- selector: '.content', post_process: { name: 'sanitize_html' }
- }
- }
-)
-```
-
-</details>
-
-<details>
- <summary>See a YAML feed config example</summary>
-
-```yml
-channel:
- # ... omitted
-selectors:
- # ... omitted
- description:
- selector: '.content'
- post_process:
- - name: sanitize_html
-```
-
-</details>
-
### Chaining post processors
Pass an array to `post_process` to chain the post processors.
-<details>
- <summary>YAML example: build the description from a template String (in Markdown) and convert that Markdown to HTML</summary>
+<details><summary>YAML example: build the description from a template String (in Markdown) and convert that Markdown to HTML</summary>
```yml
channel:
# ... omitted
selectors:
@@ -241,14 +226,51 @@
Price: %{price}
- name: markdown_to_html
```
-Note the use of `|` for a multi-line String in YAML.
+</details>
+### Post processor `gsub`
+
+The post processor `gsub` makes use of Ruby's [`gsub`](https://apidock.com/ruby/String/gsub) method.
+
+| key | type | required | note |
+| ------------- | ------ | -------- | --------------------------- |
+| `pattern` | String | yes | Can be Regexp or String. |
+| `replacement` | String | yes | Can be a [backreference](). |
+
+<details><summary>See a Ruby example</summary>
+
+```ruby
+Html2rss.feed(
+ channel: {},
+ selectors: {
+ title: { selector: 'a', post_process: [{ name: 'gsub', pattern: 'foo', replacement: 'bar' }] }
+ }
+)
+```
+
</details>
+<details><summary>See a YAML feed config example</summary>
+
+```yml
+channel:
+ # ... omitted
+selectors:
+ # ... omitted
+ title:
+ selector: "a"
+ post_process:
+ - name: "gsub"
+ pattern: "foo"
+ replacement: "bar"
+```
+
+</details>
+
## Adding `<category>` tags to an item
The `categories` selector takes an array of selector names. Each value of those
selectors will become a `<category>` on the RSS item.
@@ -288,32 +310,77 @@
- branch
```
</details>
+## Custom item GUID
+
+By default, html2rss generates a GUID from the `title` or `description`.
+
+If this does not work well, you can choose other attributes from which the GUID is build.
+The principle is the same as for the categories: pass an array of selectors names.
+
+In all cases, the GUID is a SHA1-encoded string.
+
+<details><summary>See a Ruby example</summary>
+
+```ruby
+Html2rss.feed(
+ channel: {},
+ selectors: {
+ title: {
+ # ... omitted
+ selector: 'h1'
+ },
+ link: { selector: 'a', extractor: 'href' },
+ guid: %i[link]
+ }
+)
+```
+
+</details>
+
+<details><summary>See a YAML feed config example</summary>
+
+```yml
+channel:
+ # ... omitted
+selectors:
+ # ... omitted
+ title:
+ selector: "h1"
+ link:
+ selector: "a"
+ extractor: "href"
+ guid:
+ - link
+```
+
+</details>
+
## Adding an `<enclosure>` tag to an item
-An enclosure can be any file, e.g. a image, audio or video.
+An enclosure can be any file, e.g. a image, audio or video - think Podcast.
The `enclosure` selector needs to return a URL of the content to enclose. If the extracted URL is relative, it will be converted to an absolute one using the channel's URL as base.
Since `html2rss` does no further inspection of the enclosure, its support comes with trade-offs:
1. The content-type is guessed from the file extension of the URL.
2. If the content-type guessing fails, it will default to `application/octet-stream`.
-3. The content-length will always be undetermined and thus stated as `0` bytes.
+3. The content-length will always be undetermined and therefore stated as `0` bytes.
Read the [RSS 2.0 spec](http://www.rssboard.org/rss-profile#element-channel-item-enclosure) for further information on enclosing content.
<details>
<summary>See a Ruby example</summary>
```ruby
Html2rss.feed(
channel: {},
selectors: {
- enclosure: { selector: 'img', extractor: 'attribute', attribute: 'src' }
+ enclosure: { selector: 'audio', extractor: 'attribute', attribute: 'src' }
}
)
```
</details>
@@ -325,164 +392,86 @@
channel:
# ... omitted
selectors:
# ... omitted
enclosure:
- selector: "img"
+ selector: "audio"
extractor: "attribute"
attribute: "src"
```
</details>
-
## Scraping and handling JSON responses
-Although this gem is called **html***2rss*, it's possible to scrape and process JSON.
+By default, `html2rss` assumes the URL responds with HTML. However, it can also handle JSON responses. The JSON must return an Array or Hash.
-Adding `json: true` to the channel config will convert the JSON response to XML.
+| key | required | default | note |
+| ---------- | -------- | ------- | ---------------------------------------------------- |
+| `json` | optional | false | If set to `true`, the response is parsed as JSON. |
+| `jsonpath` | optional | $ | Use [JSONPath syntax]() to select nodes of interest. |
-<details>
- <summary>See a Ruby example</summary>
+<details><summary>See a Ruby example</summary>
```ruby
Html2rss.feed(
- channel: {
- url: 'https://example.com', json: true
- },
- selectors: {} # ... omitted
+ channel: { url: 'http://domainname.tld/whatever.json', json: true },
+ selectors: { title: { selector: 'foo' } }
)
```
</details>
-<details>
- <summary>See a YAML feed config example</summary>
+<details><summary>See a YAML feed config example</summary>
-```yaml
+```yml
channel:
- url: https://example.com
+ url: "http://domainname.tld/whatever.json"
json: true
selectors:
- # ... omitted
+ title:
+ selector: "foo"
```
</details>
-<details>
- <summary>See example of a converted JSON object</summary>
+## Set any HTTP header in the request
-This JSON object:
+To set HTTP request headers, you can add them to the channel's `headers` hash. This is useful for APIs that require an Authorization header.
-```json
-{
- "data": [{ "title": "Headline", "url": "https://example.com" }]
-}
+```yml
+channel:
+ url: "https://example.com/api/resource"
+ headers:
+ Authorization: "Bearer YOUR_TOKEN"
+selectors:
+ # ... omitted
```
-converts to:
+Or for setting a User-Agent:
-```xml
-<hash>
- <data>
- <datum>
- <title>Headline</title>
- <url>https://example.com</url>
- </datum>
- </data>
-</hash>
-```
-
-Your items selector would be `data > datum`, the item's `link` selector would be `url`.
-
-Find further information in [ActiveSupport's `Hash.to_xml` documentation](https://apidock.com/rails/Hash/to_xml).
-
-</details>
-
-<details>
- <summary>See example of a converted JSON array</summary>
-
-This JSON array:
-
-```json
-[{ "title": "Headline", "url": "https://example.com" }]
-```
-
-converts to:
-
-```xml
-<objects>
- <object>
- <title>Headline</title>
- <url>https://example.com</url>
- </object>
-</objects>
-```
-
-Your items selector would be `objects > object`, the item's `link` selector would be `url`.
-
-Find further information in [ActiveSupport's `Array.to_xml` documentation](https://apidock.com/rails/Array/to_xml).
-
-</details>
-
-## Set any HTTP header in the request
-
-You can add any HTTP headers to the request to the channel URL.
-Use this to e.g. have Cookie or Authorization information sent or to spoof the User-Agent.
-
-<details>
- <summary>See a Ruby example</summary>
-
- ```ruby
- Html2rss.feed(
- channel: {
- url: 'https://example.com',
- headers: {
- "User-Agent": "html2rss-request",
- "X-Something": "Foobar",
- "Authorization": "Token deadbea7",
- "Cookie": "monster=MeWantCookie"
- }
- },
- selectors: {}
- )
- ```
-
-</details>
-
-<details>
- <summary>See a YAML feed config example</summary>
-
-```yaml
+```yml
channel:
- url: https://example.com
+ url: "https://example.com"
headers:
- "User-Agent": "html2rss-request"
- "X-Something": "Foobar"
- "Authorization": "Token deadbea7"
- "Cookie": "monster=MeWantCookie"
+ User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
selectors:
- # ...
+ # ... omitted
```
-</details>
-
-The headers provided by the channel are merged into the global headers.
-
## Usage with a YAML config file
This step is not required to work with this gem. If you're using
-[`html2rss-web`](https://github.com/gildesmarais/html2rss-web)
+[`html2rss-web`](https://github.com/html2rss/html2rss-web)
and want to create your private feed configs, keep on reading!
-First, create your YAML file, e.g. called `feeds.yml`.
-This file will contain your global config and feed configs.
+First, create a YAML file, e.g. `feeds.yml`. This file will contain your global config and multiple feed configs under the key `feeds`.
Example:
```yml
headers:
- 'User-Agent': "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1"
+ "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1"
feeds:
myfeed:
channel:
selectors:
myotherfeed:
@@ -490,48 +479,112 @@
selectors:
```
Your feed configs go below `feeds`. Everything else is part of the global config.
-Build your feeds like this:
+Find a full example of a `feeds.yml` at [`spec/feeds.test.yml`](https://github.com/html2rss/html2rss/blob/master/spec/feeds.test.yml).
+Now you can build your feeds like this:
+
+<details>
+ <summary>Build feeds in Ruby</summary>
+
```ruby
require 'html2rss'
myfeed = Html2rss.feed_from_yaml_config('feeds.yml', 'myfeed')
myotherfeed = Html2rss.feed_from_yaml_config('feeds.yml', 'myotherfeed')
```
-Find a full example of a `feeds.yml` at [`spec/config.test.yml`](https://github.com/gildesmarais/html2rss/blob/master/spec/config.test.yml).
+</details>
-## Gotchas and tips & tricks
+<details>
+ <summary>Build feeds on the command line</summary>
-- Check that the channel URL does not redirect to a mobile page with a different markup structure.
-- Do not rely on your web browser's developer console. `html2rss` does not execute JavaScript.
-- Fiddling with [`curl`](https://github.com/curl/curl) and [`pup`](https://github.com/ericchiang/pup) to find the selectors seems efficient (`curl URL | pup`).
-- [CSS selectors are quite versatile, here's an overview.](https://www.w3.org/TR/selectors-4/#overview)
+```sh
+html2rss feed feeds.yml myfeed
+html2rss feed feeds.yml myotherfeed
+```
-## Development
+</details>
-After checking out the repository, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests.
-You can also run `bin/console` for an interactive prompt that will allow you to experiment.
+## Display the RSS feed nicely in a web browser
+To display RSS feeds nicely in a web browser, you can:
+
+- add a plain old CSS stylesheet, or
+- use XSLT (e**X**tensible **S**tylesheet **L**anguage **T**ransformations).
+
+A web browser will apply these stylesheets and show the contents as described.
+
+In a CSS stylesheet, you'd use `element` selectors to apply styles.
+
+If you want to do more, then you need to create a XSLT. XSLT allows you
+to use a HTML template and to freely design the information of the RSS,
+including using JavaScript and external resources.
+
+You can add as many stylesheets and types as you like. Just add them to your global configuration.
+
<details>
- <summary>Releasing a new version</summary>
+ <summary>Ruby: a stylesheet config example</summary>
-1. `git pull`
-2. increase version in `lib/html2rss/version.rb`
-3. `bundle`
-4. `git add Gemfile.lock lib/html2rss/version.rb`
-5. `VERSION=$(ruby -e 'require "./lib/html2rss/version.rb"; puts Html2rss::VERSION')`
-6. `git commit -m "chore: release $VERSION"`
-7. `git tag v$VERSION`
-8. [`standard-changelog -f`](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/standard-changelog)
-9. `git add CHANGELOG.md && git commit --amend`
-10. `git tag v$VERSION -f`
-11. `git push && git push --tags`
+```ruby
+config = Html2rss::Config.new(
+ { channel: {}, selectors: {} }, # omitted
+ {
+ stylesheets: [
+ {
+ href: '/relative/base/path/to/style.xls',
+ media: :all,
+ type: 'text/xsl'
+ },
+ {
+ href: 'http://example.com/rss.css',
+ media: :all,
+ type: 'text/css'
+ }
+ ]
+ }
+)
+Html2rss.feed(config)
+```
+
</details>
-## Contributing
+<details>
+ <summary>YAML: a stylesheet config example</summary>
-Bug reports and pull requests are welcome on GitHub at https://github.com/gildesmarais/html2rss.
+```yml
+stylesheets:
+ - href: "/relative/base/path/to/style.xls"
+ media: "all"
+ type: "text/xsl"
+ - href: "http://example.com/rss.css"
+ media: "all"
+ type: "text/css"
+feeds:
+ # ... omitted
+```
+
+</details>
+
+Recommended further readings:
+
+- [How to format RSS with CSS on lifewire.com](https://www.lifewire.com/how-to-format-rss-3469302)
+- [XSLT: Extensible Stylesheet Language Transformations on MDN](https://developer.mozilla.org/en-US/docs/Web/XSLT)
+- [The XSLT used by html2rss-web](https://github.com/html2rss/html2rss-web/blob/master/public/rss.xsl)
+
+## Gotchas and tips & tricks
+
+- Check that the channel URL does not redirect to a mobile page with a different markup structure.
+- Do not rely on your web browser's developer console. `html2rss` does not execute JavaScript.
+- Fiddling with [`curl`](https://github.com/curl/curl) and [`pup`](https://github.com/ericchiang/pup) to find the selectors seems efficient (`curl URL | pup`).
+- [CSS selectors are versatile. Here's an overview.](https://www.w3.org/TR/selectors-4/#overview)
+
+### Contributing
+
+1. Fork it ( <https://github.com/html2rss/html2rss/fork> )
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Commit your changes (`git commit -am 'Add some feature'`)
+4. Push to the branch (`git push origin my-new-feature`)
+5. Create a new Pull Request