mcpField
C
cheerio-parsing

cheerio-parsing

github.com/mindrally数据与数据库287833 次安装3 周前更新
代码仓库

关于 cheerio-parsing

面向使用 Node.js 构建网页抓取和数据提取流程的开发者,介绍如何用 Cheerio 解析 HTML。内容涵盖选择器、DOM 遍历、安全提取、URL 解析和效率实践。

主要功能

  1. 01基于 CSS 选择器查找元素
  2. 02DOM 遍历与筛选
  3. 03提取文本、HTML 和属性
  4. 04安全处理缺失元素
  5. 05解析相对 URL
  6. 06HTML 解析性能实践

适用场景

  1. 01提取网页中的商品信息
  2. 02收集表格和列表数据
  3. 03查找分页链接
  4. 04构建可复用的抓取工具

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mindrally/skills --skill cheerio-parsing

安装后在 Claude Code 中输入/cheerio-parsing确认已加载。

详细介绍

Cheerio HTML 解析

你是 Cheerio、Node.js HTML 解析、DOM 操作和构建高效网页抓取数据提取管道方面的专家。

核心专业知识

  • Cheerio API 和类似 jQuery 的语法
  • CSS 选择器优化
  • DOM 遍历和操作
  • HTML/XML 解析策略
  • 与 HTTP 客户端(axios、got、node-fetch)集成
  • 高效处理大型文档,降低内存占用
  • 数据提取模式和最佳实践

核心原则

  • 编写清晰、模块化的提取函数
  • 使用高效的选择器,尽可能降低解析开销
  • 妥善处理格式错误的 HTML
  • 为缺少元素的情况实现适当的错误处理
  • 设计可复用的抓取工具
  • 在适当情况下遵循函数式编程模式

基本设置

npm install cheerio axios
加载 HTML
const cheerio = require('cheerio');
const axios = require('axios');

// Load from string
const $ = cheerio.load('<html><body><h1>Hello</h1></body></html>');

// Load with options
const $ = cheerio.load(html, {
  xmlMode: false,           // Parse as XML
  decodeEntities: true,     // Decode HTML entities
  lowerCaseTags: false,     // Keep tag case
  lowerCaseAttributeNames: false
});

// Fetch and parse
async function fetchAndParse(url) {
  const response = await axios.get(url);
  return cheerio.load(response.data);
}

选择元素

CSS 选择器
// By tag
$('h1')

// By class
$('.article')

// By ID
$('#main-content')

// By attribute
$('[data-id="123"]')
$('a[href^="https://"]')  // Starts with
$('a[href$=".pdf"]')      // Ends with
$('a[href*="example"]')   // Contains

// Combinations
$('div.article > h2')     // Direct child
$('div.article h2')       // Any descendant
$('h2 + p')               // Adjacent sibling
$('h2 ~ p')               // General sibling

// Pseudo-selectors
$('li:first-child')
$('li:last-child')
$('li:nth-child(2)')
$('li:nth-child(odd)')
$('tr:even')
$('input:not([type="hidden"])')
$('p:contains("specific text")')
多个选择器
// Select multiple types
$('h1, h2, h3')

// Chain selections
$('.article').find('.title')

提取数据

文本内容
// Get text (includes child text)
const text = $('h1').text();

// Get trimmed text
const text = $('h1').text().trim();

// Get HTML
const html = $('div.content').html();

// Get outer HTML
const outerHtml = $.html($('div.content'));
属性
// Get attribute
const href = $('a').attr('href');
const src = $('img').attr('src');

// Get data attributes
const id = $('div').data('id');  // data-id attribute

// Check if attribute exists
const hasClass = $('div').hasClass('active');
多个元素
// Iterate with each
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')
  });
});

// Map to array
const titles = $('h2').map((i, el) => $(el).text()).get();

// Filter elements
const featured = $('.product').filter('.featured');

// First/Last
const first = $('li').first();
const last = $('li').last();

// Get by index
const third = $('li').eq(2);

DOM 遍历

导航
// Parent
$('span').parent()
$('span').parents()          // All ancestors
$('span').parents('.container')  // Specific ancestor
$('span').closest('.wrapper')    // Nearest ancestor matching selector

// Children
$('ul').children()           // Direct children
$('ul').children('li.active') // Filtered children
$('div').contents()          // Including text nodes

// Siblings
$('li').siblings()
$('li').next()
$('li').nextAll()
$('li').prev()
$('li').prevAll()
筛选
// Filter by selector
$('li').filter('.active')

// Filter by function
$('li').filter((i, el) => $(el).data('price') > 100)

// Find within selection
$('.article').find('img')

// Check conditions
$('li').is('.active')  // Returns boolean
$('li').has('span')    // Has descendant matching selector

数据提取模式

表格提取
function extractTable(tableSelector) {
  const $ = this;
  const headers = [];
  const rows = [];

  // Get headers
  $(tableSelector).find('th').each((i, el) => {
    headers.push($(el).text().trim());
  });

  // Get rows
  $(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;
}
列表提取
function extractList(selector, itemExtractor) {
  return $(selector).map((i, el) => itemExtractor($(el))).get();
}

// Usage
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')
}));
分页链接
function extractPaginationLinks() {
  return $('.pagination a')
    .map((i, el) => $(el).attr('href'))
    .get()
    .filter(href => href && !href.includes('#'));
}

处理缺失数据

// Safe extraction with defaults
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;
}

// Optional chaining pattern
const price = $('.price').first().text()?.trim() || 'N/A';

URL 解析

const { URL } = require('url');

function resolveUrl(baseUrl, relativeUrl) {
  if (!relativeUrl) return null;
  try {
    return new URL(relativeUrl, baseUrl).href;
  } catch {
    return relativeUrl;
  }
}

// Usage
const baseUrl = 'https://example.com/products/';
$('a').each((i, el) => {
  const href = $(el).attr('href');
  const absoluteUrl = resolveUrl(baseUrl, href);
  console.log(absoluteUrl);
});

性能优化

// Cache selections
const $products = $('.product');
$products.each((i, el) => {
  const $product = $(el);  // Wrap once
  // Use $product multiple times
});

// Limit parsing scope
const $article = $('.article');
const title = $article.find('.title').text();  // Searches only within article

// Use specific selectors
// Good
$('div.product > h2.title')
// Less efficient
$('div').find('.product').find('h2').filter('.title')

完整抓取示例

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;
}

// With error handling
async function safeScrape(url) {
  try {
    return await scrapeProducts(url);
  } catch (error) {
    console.error(`Failed to scrape ${url}:`, error.message);
    return [];
  }
}

关键依赖项

  • cheerio
  • axios(HTTP 客户端)
  • got(备用 HTTP 客户端)
  • node-fetch(Node.js 的 fetch API)
  • p-limit(并发控制)

最佳实践

  1. 始终妥善处理缺失元素
  2. 使用特定的选择器以提升性能
  3. 重用元素时缓存由 jQuery 封装的元素
  4. 规范化提取的文本(去除空白)
  5. 将相对 URL 解析为绝对 URL
  6. 验证提取的数据类型
  7. 抓取多个页面时实施速率限制
  8. 使用适当的 User-Agent 标头
  9. 处理字符编码问题
  10. 记录提取失败情况以便调试

评论

支持 Markdown