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