Home

Awesome

hred

<a href="https://www.npmjs.org/package/hred"><img src="https://img.shields.io/npm/v/hred.svg?style=flat-square&labelColor=CC9252&color=black" alt="npm version"></a>

hred (html reduce) is a command-line tool to extract data from HTML. It reads HTML from the standard input and outputs the JSON produced by a qsx query:

> curl https://danburzo.ro/rolodex/ | hred "article a { @href, @.textContent }"
[
  {
    "href": "http://www.3quarksdaily.com/",
    ".textContent": "3 Quarks Daily"
  },
  {
    "href": "http://50watts.com",
    ".textContent": "50 Watts"
  },
  {
    "href": "http://aworkinglibrary.com/",
    ".textContent": "A Working Library"
  },
  ...
]

The qsx documentation describes the kinds of queries you can make with hred, but if you're familiar with CSS selectors you're mostly good to go.

Installation

hred runs on Node.js. You can find hred in the npm registry:

# install hred globally with npm:
npm install -g hred

# install hred globally with yarn:
yarn global add hred

# run hred without installing it:
npx hred 

Usage

hred accepts a qsx query string:

curl https://en.wikipedia.org/wiki/Banana | hred "img { @alt, @src }"

[
  {
    "alt": "Page semi-protected",
    "src": "//upload.wikimedia.org/wikipedia/en/thumb/1/1b/Semi-protection-shackle.svg/20px-Semi-protection-shackle.svg.png"
  },
  {
    "alt": "Banana and cross section.jpg",
    "src": "//upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Banana_and_cross_section.jpg/250px-Banana_and_cross_section.jpg"
  },
  ...
]

hred has the single, modest purpose of extracting parts of HTML as JSON. Because the qsx query language is a lightweight extension to the CSS selector syntax used by the Element.querySelectorAll() DOM method, hred offers only limited reshaping of the resulting JSON via aliases. The tool is designed to be piped to something like jq if further JSON processing is necessary.

hred has a few options available:

OptionDescription
-c, --concatif the result is an array, return it as concatenated JSON records, to make it easier to collate several results together
-f <queryfile>, --file=<queryfile>read the query from an external file instead of passing it as an operand
-h, --helpprint help message
-r, --rawa complement to -c that returns raw (unquoted) strings when the result is an array of strings
-u <url>, --url=<url>add the base URL against which the HTML should be evaluated; influences the value of the DOM properties @.href, @.src when the HTML attributes are relative
-V, --versiondisplay the current version
-x, --xmlparse the input as XML rather than HTML

A real-life example

Let's take a web page that uses atomic, presentational CSS rather than semantic CSS classes (and thus makes it more challenging to extract data), such as my starred repos page. To extract info about the repositories, at the time of writing:

curl https://github.com/danburzo\?tab\=stars | hred "
.mb-1 {
	h3 a ...{ 
		@href => url , 
		@.textContent => title 
	}, 
	^ :scope ~ .py-1 @.textContent => description 
}"

Let's break the query apart:

For each element with the class mb-1:

  1. on the one hand, find <a> elements nested into <h3>s:
    1. read their href HTML attribute as url and their textContent DOM property as title;
    2. merge the resulting object into the current scope with >> .;
  2. on the other hand, find the first (^) subsequent element (:scope ~) that matches the class py-1
    1. extract its textContent as description.

The resulting JSON, abridged:

[
  {
    "url": "/urfave/cli",
    "title": "\n        urfave / cli\n      ",
    "description": "\n      \n        A simple, fast, and fun package for building command line apps in Go\n      \n  "
  },

A note on security

hred uses as its DOM environment jsdom, which has the ability to run the JavaScript included in web pages. Because scripts specially crafted to attack jsdom may potentially evade the sandbox to which their execution is confined and access your machine through Node.js APIs, script execution is disabled; furthermore, external resources (scripts, images, stylesheets, iframes) are not fetched. Even with these precautions, be careful with what web pages you process with hred; when in doubt, inspect the page's source code beforehand.

Related projects

You might be interested in these: