| name | cheerio-parsing |
| description | Expert guidance for HTML/XML parsing using Cheerio in Node.js with best practices for DOM traversal, data extraction, and efficient scraping pipelines. |
Cheerio HTML Parsing
You are an expert in Cheerio, Node.js HTML parsing, DOM manipulation, and building efficient data extraction pipelines for web scraping.
Core Expertise
- Cheerio API and jQuery-like syntax
- CSS selector optimization
- DOM traversal and manipulation
- HTML/XML parsing strategies
- Integration with HTTP clients (axios, got, node-fetch)
- Memory-efficient processing of large documents
- Data extraction patterns and best practices
Key Principles
- Write clean, modular extraction functions
- Use efficient selectors to minimize parsing overhead
- Handle malformed HTML gracefully
- Implement proper error handling for missing elements
- Design reusable scraping utilities
- Follow functional programming patterns where appropriate
Basic Setup
npm install cheerio axios
Loading HTML
const cheerio = require('cheerio');
const axios = require('axios');
const $ = cheerio.load('<html><body><h1>Hello</h1></body></html>');
const $ = cheerio.load(html, {
xmlMode: false,
decodeEntities: true,
lowerCaseTags: false,
lowerCaseAttributeNames: false
});
async function fetchAndParse(url) {
const response = await axios.get(url);
return cheerio.load(response.data);
}
Selecting Elements
CSS Selectors
$('h1')
$('.article')
$('#main-content')
$('[data-id="123"]')
$('a[href^="https://"]')
$('a[href$=".pdf"]')
$('a[href*="example"]')
$('div.article > h2')
$('div.article h2')
$('h2 + p')
$('h2 ~ p')
$('li:first-child')
$('li:last-child')
$('li:nth-child(2)')
$('li:nth-child(odd)')
$('tr:even')
$('input:not([type="hidden"])')
$('p:contains("specific text")')
Multiple Selectors
$('h1, h2, h3')
$('.article').find('.title')
Extracting Data
Text Content
const text = $('h1').text();
const text = $('h1').text().trim();
const html = $('div.content').html();
const outerHtml = $.html($('div.content'));
Attributes
const href = $('a').attr('href');
const src = $('img').attr('src');
const id = $('div').data('id');
const hasClass = $('div').hasClass('active');
Multiple Elements
const items = [];
$('.product').each((index, element) => {
items.push({
name: $(element).find('.name').text().trim(),
price: $(element).find('.price').text().trim(),
url: $(element).find('a').attr('href')
});
});
const titles = $('h2').map((i, el) => $(el).text()).get();
const featured = $('.product').filter('.featured');
const first = $('li').first();
const last = $('li').last();
const third = $('li').eq(2);
DOM Traversal
Navigation
$('span').parent()
$('span').parents()
$('span').parents('.container')
$('span').closest('.wrapper')
$('ul').children()
$('ul').children('li.active')
$('div').contents()
$('li').siblings()
$('li').next()
$('li').nextAll()
$('li').prev()
$('li').prevAll()
Filtering
$('li').filter('.active')
$('li').filter((i, el) => $(el).data('price') > 100)
$('.article').find('img')
$('li').is('.active')
$('li').has('span')
Data Extraction Patterns
Table Extraction
function extractTable(tableSelector) {
const $ = this;
const headers = [];
const rows = [];
$(tableSelector).find('th').each((i, el) => {
headers.push($(el).text().trim());
});
$(tableSelector).find('tbody tr').each((i, row) => {
const rowData = {};
$(row).find('td').each((j, cell) => {
rowData[headers[j]] = $(cell).text().trim();
});
rows.push(rowData);
});
return rows;
}
List Extraction
function extractList(selector, itemExtractor) {
return $(selector).map((i, el) => itemExtractor($(el))).get();
}
const products = extractList('.product', ($el) => ({
name: $el.find('.name').text().trim(),
price: parseFloat($el.find('.price').text().replace('$', '')),
image: $el.find('img').attr('src'),
link: $el.find('a').attr('href')
}));
Pagination Links
function extractPaginationLinks() {
return $('.pagination a')
.map((i, el) => $(el).attr('href'))
.get()
.filter(href => href && !href.includes('#'));
}
Handling Missing Data
function safeText(selector, defaultValue = '') {
const el = $(selector);
return el.length ? el.text().trim() : defaultValue;
}
function safeAttr(selector, attr, defaultValue = null) {
const el = $(selector);
return el.length ? el.attr(attr) : defaultValue;
}
const price = $('.price').first().text()?.trim() || 'N/A';
URL Resolution
const { URL } = require('url');
function resolveUrl(baseUrl, relativeUrl) {
if (!relativeUrl) return null;
try {
return new URL(relativeUrl, baseUrl).href;
} catch {
return relativeUrl;
}
}
const baseUrl = 'https://example.com/products/';
$('a').each((i, el) => {
const href = $(el).attr('href');
const absoluteUrl = resolveUrl(baseUrl, href);
console.log(absoluteUrl);
});
Performance Optimization
const $products = $('.product');
$products.each((i, el) => {
const $product = $(el);
});
const $article = $('.article');
const title = $article.find('.title').text();
$('div.product > h2.title')
$('div').find('.product').find('h2').filter('.title')
Complete Scraping Example
const cheerio = require('cheerio');
const axios = require('axios');
async function scrapeProducts(url) {
const response = await axios.get(url, {
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; MyScraper/1.0)'
}
});
const $ = cheerio.load(response.data);
const products = [];
$('.product-card').each((index, element) => {
const $el = $(element);
products.push({
name: $el.find('.product-title').text().trim(),
price: parseFloat(
$el.find('.price').text().replace(/[^0-9.]/g, '')
),
rating: parseFloat($el.find('.rating').attr('data-rating')) || null,
image: $el.find('img').attr('src'),
url: new URL($el.find('a').attr('href'), url).href,
inStock: !$el.find('.out-of-stock').length
});
});
return products;
}
async function safeScrape(url) {
try {
return await scrapeProducts(url);
} catch (error) {
console.error(`Failed to scrape ${url}:`, error.message);
return [];
}
}
Key Dependencies
- cheerio
- axios (HTTP client)
- got (alternative HTTP client)
- node-fetch (fetch API for Node.js)
- p-limit (concurrency control)
Best Practices
- Always handle missing elements gracefully
- Use specific selectors for better performance
- Cache jQuery-wrapped elements when reusing
- Normalize extracted text (trim whitespace)
- Resolve relative URLs to absolute
- Validate extracted data types
- Implement rate limiting when scraping multiple pages
- Use appropriate User-Agent headers
- Handle character encoding issues
- Log extraction failures for debugging