mcpField
C
cheerio-parsing

cheerio-parsing

github.com/mindrallyデータ・データベース287833 回インストール3 週間前に更新
リポジトリ

cheerio-parsing について

Node.js で Web スクレイピングやデータ抽出パイプラインを作る開発者向けの、Cheerio による HTML 解析ガイドです。セレクター、DOM 操作、安全な抽出、URL 解決、効率化を扱います。

主な機能

  1. 01CSS セレクターによる要素検索
  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. デバッグ用に抽出の失敗を記録します

コメント