What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Cheerio’s normal CSS selector entry point, $(), with an attribute selector such as [data-kind="note"]. Presence selectors ([attr]), exact matches ([attr="value"]), and prefix, suffix, or substring operators let you find the elements you need; then use attr(), text(), each(), map(), find(), and filter() to extract or narrow results.

Basic attribute selection

Cheerio uses CSS selectors—the same selector syntax used in a stylesheet or with document.querySelectorAll. Load the HTML, pass a selector to $(), and inspect the returned selection.

import * as cheerio from 'cheerio';

const html = `
  <article>
    <a data-kind="note" href="/one">First</a>
    <a data-kind="link" href="https://example.com/two">Second</a>
    <a href="/three">Third</a>
  </article>
`;

const $ = cheerio.load(html);

const notes = $('[data-kind="note"]');
console.log(notes.length);       // 1
console.log(notes.attr('href')); // /one
console.log(notes.text());       // First

The selector is evaluated against the HTML supplied to cheerio.load(); Cheerio does not automatically open a URL or execute the page’s JavaScript.

Attribute selector patterns you can use

Test whether an attribute exists

[data-kind] matches every element that has a data-kind attribute, regardless of its value. This is the safest first diagnostic when an exact selector returns zero results.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option
const marked = $('[data-kind]');
console.log(marked.length);

Match an exact value

[data-kind="note"] requires both the attribute and the exact value. Add a tag name when you want to exclude other element types: a[data-kind="note"].

const noteLinks = $('a[data-kind="note"]');

Quote values containing punctuation, spaces, or other characters that could be interpreted as selector syntax. Quoting also makes the intended match unambiguous.

Match prefixes, suffixes, and substrings

  • [href^="https://"] matches values beginning with https://.
  • [href$=".pdf"] matches values ending with .pdf.
  • [href*="example"] matches values containing example.
const secureLinks = $('a[href^="https://"]');
const pdfLinks = $('a[href$=".pdf"]');
const exampleLinks = $('a[href*="example"]');

Match tokens and language prefixes

[class~="featured"] treats the value as a space-separated token list, so it matches an element whose class list contains featured but not one whose class is featured-card. [lang|="en"] matches en and values beginning with en-, such as en-US.

Namespaced attributes

Escape the colon when selecting a namespaced attribute:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const main = $('[xml\:id="main"]');

Combine attributes with other CSS selectors

Attribute selectors can be combined with tags, classes, relationships, and comma-separated alternatives.

// Descendants of article
const notesInArticles = $('article a[data-kind="note"]');

// Direct children only
const navLinks = $('nav > a[data-kind="link"]');

// Either heading level
const titles = $('h1[data-role="title"], h2[data-role="title"]');

A descendant space allows any depth; > restricts the match to direct children. You can add classes or IDs in the usual way, for example div.card[data-state="open"].

Narrow an existing selection with traversal methods

When you already have a broad selection, traversal methods make the intent clearer than repeating a long selector.

const articles = $('article');
const notes = articles.find('[data-kind="note"]');
const featured = notes.filter('.featured');
const first = notes.first();
const last = notes.last();
const third = notes.eq(2);

find() searches within the current selection and returns a new selection. filter() keeps only elements matching the supplied selector. first(), last(), and eq(index) select a particular item. Cheerio’s selector engine also supports jQuery-style positional forms such as :first, :last, and :eq(n); these are Cheerio extensions rather than standard browser CSS.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read attributes and text from matches

Read one value

attr('href') reads the attribute from the first element in the selection. It returns undefined when no matching element or attribute exists.

const link = $('a[data-kind="note"]');
const href = link.attr('href');
const label = link.text();
console.log({ href, label });

Extract every match with each()

Use each() when you want to process every element and can perform work inside the callback.

const rows = [];
$('a[data-kind]').each((index, element) => {
  rows.push({
    index,
    kind: $(element).attr('data-kind'),
    href: $(element).attr('href'),
    text: $(element).text().trim()
  });
});
console.log(rows);

Build an array with map()

map() is convenient when each element becomes one value. Call get() to convert the Cheerio collection into a normal JavaScript array.

const links = $('a[data-kind]').map((index, element) => ({
  kind: $(element).attr('data-kind'),
  href: $(element).attr('href'),
  text: $(element).text().trim()
})).get();

Use prop() when you need a property supported by Cheerio rather than the literal attribute value. For ordinary HTML attributes such as href, attr() is usually the appropriate API.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Find several attribute conditions in one pass

Selectors can express multiple requirements. For example, this finds only links inside a navigation element that have an HTTPS URL and a specific data role:

const externalNavLinks = $('nav a[data-role="external"][href^="https://"]');

Use a comma-separated selector when the alternatives are independent:

const headings = $('[data-role="title"], [data-role="subtitle"]');

For a broad result followed by conditional logic, select first and filter in JavaScript. This is useful when the rule is not expressible in CSS or requires parsing the value.

const numbered = $('[data-id]').filter((index, element) => {
  const id = $(element).attr('data-id');
  return id ? /^item-d+$/.test(id) : false;
});

Safely build selectors from input

Do not concatenate untrusted or arbitrary values into a selector without escaping selector-special characters. Periods, colons, spaces, quotation marks, brackets, and backslashes can change the meaning of the selector or make it invalid. Prefer a selector-escaping utility appropriate to your application, or avoid interpolation by selecting [data-key] and comparing the attribute value in JavaScript.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const wanted = 'note:primary';
const matches = $('[data-kind]').filter((index, element) =>
  $(element).attr('data-kind') === wanted
);

This comparison approach avoids selector parsing entirely. If you do interpolate a value, escape it according to the selector engine’s rules before constructing the selector.

Why an attribute selector returns nothing

Verify the input HTML

The most common problem is that the string passed to cheerio.load() is not the HTML you think it is. Log a short fragment, the document title, or the total element count before debugging the selector.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
const $ = cheerio.load(html);
console.log($.html().slice(0, 500));
console.log('all elements:', $('*').length);
console.log('attribute elements:', $('[data-kind]').length);

Reduce the selector step by step

  1. Start with the attribute-presence test, such as [data-kind].
  2. Add the tag name: a[data-kind].
  3. Add the exact value: a[data-kind="note"].
  4. Add relationship constraints such as article a[data-kind="note"].

The first step that changes the count identifies the part of the selector that needs correction.

Check spelling, case, and value formatting

HTML attribute names are generally case-insensitive, but values are data-dependent. A value of Note does not necessarily equal note. Watch for whitespace, URL encoding, missing prefixes, and attributes whose value is generated differently than expected.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Account for client-side rendering

React, Vue, and other frameworks may create nodes only after browser JavaScript runs. Cheerio sees the response HTML you provide; it does not run that application code. Obtain server-rendered HTML, call the underlying data API, or use a browser-capable capture step before passing the resulting HTML to Cheerio.

Check the attribute type

An element may expose a browser property without carrying the corresponding source attribute. Inspect the raw HTML and choose attr() for source attributes or prop() for supported properties.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

A reusable extraction function

The following function returns normalized records and handles missing attributes without throwing:

import * as cheerio from 'cheerio';

export function findByAttribute(html, attribute, expectedValue) {
  const $ = cheerio.load(html);
  const selector = `[${attribute}]`;

  return $(selector).filter((index, element) => {
    if (expectedValue === undefined) return true;
    return $(element).attr(attribute) === expectedValue;
  }).map((index, element) => ({
    tag: element.name,
    value: $(element).attr(attribute),
    text: $(element).text().trim()
  })).get();
}

console.log(findByAttribute(html, 'data-kind', 'note'));

If attribute itself can come from untrusted input, validate it against an allowlist of expected attribute names before constructing the presence selector.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Performance and reliability considerations

  • Use the narrowest selector that expresses your requirement; it reduces traversal and makes accidental matches less likely.
  • For repeated extraction, select once and iterate rather than reparsing the same HTML.
  • Keep network fetching separate from parsing so you can log response status, content type, and the exact HTML given to Cheerio.
  • Expect malformed or partial HTML from failed requests and verify that the response is actually HTML rather than a bot-check page, JSON error, or login form.
  • When pages are large, extract the required fields during one pass and discard the document when finished instead of retaining many Cheerio selections.

Or skip the browser setup

If the page requires a real browser to render content or to clear consent UI before you obtain HTML or an image, ScreenshotNeo provides a one-request screenshot API. It accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server supplies take_screenshot, get_page_info, and capture_pdf tools to Claude, Cursor, and other MCP clients.

See the ScreenshotNeo documentation for parameter details. A direct cURL request is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The same call in Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

And in Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Every plan includes full-page capture, element selection, custom CSS and JavaScript, waits, request blocking, cookies and headers, device and viewport controls, PDFs, signed links, asynchronous jobs, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. The service also accepts parameter names used by other screenshot APIs.

The Free plan includes 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free. Create a free ScreenshotNeo account to start without a card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Does Cheerio execute JavaScript from the page?

No. Cheerio parses the HTML string you provide. For client-rendered nodes, obtain server-rendered HTML or the underlying API data first.

What is the difference between [attr] and [attr=”value”]?

[attr] tests only for attribute presence, while [attr=”value”] requires an exact attribute value.

How do I get an attribute from every matched element?

Iterate with each() and call $(element).attr(‘name’), or use map(…).get() to create an array.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.