🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Book a DemoInstallSign in
Socket

cparse

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cparse

一个基于 Cheerio 的 HTML 解析和数据提取工具库

2.2.0
latest
Source
npm
Version published
Weekly downloads
206
32.05%
Maintainers
1
Weekly downloads
 
Created
Source

cparse

npm version License: MIT Node.js Version

一个基于 Cheerio 的强大 HTML 解析和数据提取工具库,专为简化网页数据抓取而设计。

✨ 核心特性

🎯 语法糖增强

  • 属性提取语法selector@attribute - 直接提取属性值
  • 数组提取语法[selector] - 获取所有匹配元素
  • 标准 CSS 支持:完全兼容 Cheerio 原生 CSS 选择器
  • 自定义伪选择器:not-empty - 扩展的伪选择器

🔧 强大的过滤器系统

  • 30+ 内置过滤器:数据类型转换、字符串处理、数组操作等
  • 过滤器链selector | filter1 | filter2 - 链式数据处理
  • 自定义过滤器:支持扩展自定义过滤器

🚀 Cheerio 功能扩展

  • 扩展方法.string(), .nextNode(), .extract(), .extractAll()
  • HTTP 集成:Axios 和 Got 客户端无缝集成
  • URL 处理:相对 URL 自动转换为绝对 URL

🛡️ 企业级特性

  • 完善的错误处理:多种错误类型和详细错误信息
  • 高性能设计:查询缓存、批量处理优化
  • TypeScript 支持:完整的类型定义
  • 全面测试:200+ 测试用例保证质量

📦 安装

npm install cparse

系统要求:Node.js >= 18.17.0

🚀 快速开始

基础用法

const { loadCheerio, parse } = require('cparse');

const html = '<div class="title">Hello World</div>';
const $ = loadCheerio(html);

// 传统用法
const title = parse('.title', $); // "Hello World"

// 🎯 新增:简化语法 - 直接在 $ 实例上调用 parse
const title2 = $.parse('.title'); // "Hello World"

// 数组提取(语法糖)
const items = $.parse('[.item]'); // 所有 .item 元素的文本数组

// 属性提取(语法糖)
const links = $.parse('[a@href]'); // 所有链接的 href 属性数组

// 过滤器链
const price = $.parse('.price | trim | float'); // 文本 -> 去空格 -> 转浮点数

🎯 简化语法对比

// ❌ 传统用法:需要传递 $ 参数
const title = parse('.title', $);
const data = parse({ title: '.title', count: '.count | int' }, $);

// ✅ 简化用法:直接在 $ 实例上调用
const title = $.parse('.title');
const data = $.parse({ title: '.title', count: '.count | int' });

结构化数据提取

const html = `
<div class="product">
  <h2 class="title">iPhone 15</h2>
  <span class="price">$999.00</span>
  <div class="rating" data-score="4.5">★★★★☆</div>
</div>
`;

const $ = loadCheerio(html);

// 使用简化语法提取结构化数据
const product = $.parse({
  title: '.title',
  price: '.price | regex:\\d+\\.\\d+ | float',
  rating: '.rating@data-score | float'
});

console.log(product);
// { title: "iPhone 15", price: 999.00, rating: 4.5 }

🔗 HTTP 客户端集成

Axios 集成

const axios = require('axios');
const { cheerioHookForAxios, parse } = require('cparse');

const client = axios.create();
cheerioHookForAxios(client);

// 响应自动包含 $ 属性,可以直接使用简化语法
const response = await client.get('https://example.com');
const title = response.$.parse('title');
const links = response.$.parse('[a@href]');

Got 集成

const got = require('got');
const { cheerioHookForGot, parse } = require('cparse');

const client = got.extend({});
cheerioHookForGot(client);

const response = await client.get('https://example.com');
const data = response.$.parse({
  title: 'title',
  description: 'meta[name="description"]@content'
});

🎯 简化语法 - 直接在 $ 实例上调用

v2.0.2+ 新增功能:现在可以直接在 Cheerio 实例上调用 parse 方法,无需传递 $ 参数!

语法对比

传统用法简化用法说明
parse('.title', $)$.parse('.title')基本选择器
parse('[.item]', $)$.parse('[.item]')数组提取
parse('a@href', $)$.parse('a@href')属性提取
parse('.price | float', $)$.parse('.price | float')过滤器链
parse({...}, $)$.parse({...})结构化数据

使用示例

const { loadCheerio } = require('cparse');
const $ = loadCheerio('<div class="title">Hello</div>');

// ✅ 推荐:使用简化语法
const title = $.parse('.title');
const data = $.parse({
  title: '.title',
  items: '[.item]',
  link: 'a@href'
});

// ❌ 传统用法(仍然支持)
const { parse } = require('cparse');
const title2 = parse('.title', $);

🎯 核心语法糖功能

cparse 的核心价值在于提供简洁的语法糖,简化常见的数据提取操作:

1. 属性提取语法 @

// 传统 Cheerio 写法
$('a').map((i, el) => $(el).attr('href')).get();

// cparse 简化语法
$.parse('[a@href]');

2. 数组提取语法 []

// 传统 Cheerio 写法
$('.item').map((i, el) => $(el).text()).get();

// cparse 简化语法
$.parse('[.item]');

3. 标准 CSS 选择器支持

// 完全支持 Cheerio 原生 CSS 选择器
$.parse('div.active');           // 类选择器
$.parse('input[type="text"]');   // 属性选择器
$.parse('li:first-child');       // 伪选择器

4. 自定义伪选择器

// :not-empty 伪选择器(Cheerio 原生不支持)
parse('p:not-empty', $); // 转换为 p:not(:empty)

🔧 强大的过滤器系统

cparse 提供了 30+ 内置过滤器,支持链式调用进行复杂的数据处理:

// 过滤器链示例
parse('.price | trim | regex:\\d+\\.\\d+ | float', $);
// 文本 -> 去空格 -> 正则提取 -> 转浮点数

📊 过滤器分类

数据类型转换

过滤器功能示例
int转换为整数parse('.count | int', $)
float转换为浮点数parse('.price | float', $)
bool转换为布尔值parse('.active | bool', $)

字符串处理

过滤器功能示例
trim去除首尾空白parse('.title | trim', $)
slice字符串切片parse('.text | slice:0:10', $)
regex正则表达式匹配parse('.text | regex:\\d+', $)
replace字符串替换parse('.text | replace:old:new', $)
split字符串分割parse('.text | split:,', $)
upper/lower大小写转换parse('.text | upper', $)
capitalize首字母大写parse('.text | capitalize', $)
title标题格式化parse('.text | title', $)

数组处理

过滤器功能示例
length获取长度parse('[.items] | length', $)
first/last首/末元素parse('[.items] | first', $)
unique数组去重parse('[.items] | unique', $)
sort数组排序parse('[.items] | sort', $)
compact过滤空值parse('[.items] | compact', $)
join数组连接parse('[.items] | join:-', $)

特殊处理

过滤器功能示例
date日期解析parse('.date | date', $)
size尺寸解析parse('.filesize | size', $)
number数字格式化parse('.price | number:2', $)
default提供默认值parse('.optional | default:"N/A"', $)

使用 default 过滤器处理缺失值

在抓取数据时,经常会遇到某些字段缺失的情况。如果一个选择器没有匹配到任何元素,解析结果通常是 null,这可能导致后续的过滤器(如 float)出错。使用 default 过滤器可以优雅地处理这种情况:

const { loadCheerio } = require('cparse');

const html = `
<div class="product">
  <span class="name">Product A</span>
  <span class="price">$19.99</span>
</div>
<div class="product">
  <span class="name">Product B</span>
  // 价格缺失
</div>
`;

const $ = loadCheerio(html);

const products = $.parse(['.product', {
  name: '.name',
  // 如果 .price 不存在,结果为 null,float(null) 会是 NaN
  // 使用 default 过滤器提供一个默认值 0
  price: '.price | float | default:0'
}]);

console.log(products);
// [
//   { name: 'Product A', price: 19.99 },
//   { name: 'Product B', price: 0 }
// ]

📚 API 参考

核心函数

loadCheerio(html, options?, baseUrl?)

加载 HTML 并返回扩展的 Cheerio 实例

const $ = loadCheerio('<div>Hello</div>', {}, 'https://example.com');

parse(rule, $, filters?)

数据解析核心函数

// 字符串规则
parse('h1', $)

// 对象规则
parse({ title: 'h1', links: '[a@href]' }, $)

// 数组规则(分割器语法)
parse(['[.item]', { name: '.name', price: '.price | float' }], $)

HTTP 集成

cheerioHookForAxios(instance, options?)

为 Axios 添加 Cheerio 支持

cheerioHookForGot(instance, options?)

为 Got 添加 Cheerio 支持

🎯 查询语法详解

基础语法

语法说明示例
selector标准 CSS 选择器parse('h1', $)
selector@attr属性提取语法糖parse('a@href', $)
[selector]数组提取语法糖parse('[.item]', $)
selector | filter过滤器链parse('.price | float', $)

语法糖功能

1. 标准 CSS 选择器支持

// 完全支持 Cheerio 原生 CSS 选择器
parse('div.active', $)           // 类选择器
parse('input[type="text"]', $)   // 属性选择器
parse('li:first-child', $)       // 伪选择器

2. 自定义伪选择器

// 语法糖(cparse 扩展)
$.parse('p:not-empty')
// 转换为 Cheerio 原生
$.parse('p:not(:empty)')

3. 复杂选择器支持

// 直接使用 Cheerio 原生选择器
parse('nav > ul > li:first-child', $)
parse('input[type="text"]:focus', $)
parse('p:contains("重要")', $)

高级用法

结构化数据提取

const data = parse({
  title: 'h1',
  price: '.price | float',
  tags: '[.tag]',
  link: 'a@href'
}, $);

分割器语法(列表处理)

const items = parse([
  '[.product]',  // 分割器:每个 .product 元素
  {
    name: '.name',
    price: '.price | float',
    inStock: '.stock | bool'
  }
], $);

函数处理

const result = parse([
  '.content',
  text => text.toUpperCase(),
  text => text.trim()
], $);

🚀 Cheerio 扩展方法

cparse 为 Cheerio 添加了便捷的扩展方法:

扩展方法列表

方法功能示例
.string()纯文本内容(不含子元素标签)$('.content').string()
.nextNode()下一个兄弟节点的文本$('.label').nextNode()
.extract(attr)提取单个元素的属性/内容$('.item').extract('href')
.extractAll(attr)提取所有元素的属性/内容$('.items').extractAll('text')

特殊属性值

extract()extractAll() 中可使用的特殊属性:

  • text: 文本内容
  • html: HTML 内容
  • outerHtml: 包含元素本身的 HTML
  • string: 纯文本内容
  • nextNode: 下一个兄弟节点文本

🛡️ 错误处理

cparse 提供完善的错误处理机制:

错误类型

  • QueryParseError: 查询语法错误
  • FilterError: 过滤器执行错误
  • ValidationError: 参数验证错误
try {
  parse('.text | unknownFilter', $);
} catch (error) {
  if (error.name === 'FilterError') {
    console.log(`过滤器错误: ${error.filterName}`);
    console.log(`可用过滤器: ${error.context.availableFilters}`);
  }
}

⚡ 性能优化

自动查询缓存

// 第一次解析会缓存结果
parse('h1', $);
// 第二次使用相同查询直接使用缓存
parse('h1', $); // 更快

批量处理建议

// ✅ 推荐:一次性提取所有数据
const data = parse({
  titles: '[h1]',
  links: '[a@href]',
  prices: '[.price | float]'
}, $);

// ❌ 避免:多次单独查询

🔄 重构优化

v2.0.0 重大更新

🎯 核心优化

  • 移除重复实现:删除与 Cheerio 原生功能重复的代码
  • 专注语法糖:保留真正有价值的语法糖功能
  • 性能提升:直接使用 Cheerio 原生选择器,性能更优
  • 代码简化:代码量减少 40%,维护性大幅提升

🚀 保留的核心价值

  • ✅ 属性提取语法:selector@attribute
  • ✅ 数组提取语法:[selector]
  • ✅ 标准 CSS 支持:完全兼容 Cheerio 原生选择器
  • ✅ 自定义伪选择器::not-empty
  • ✅ 强大的过滤器系统
  • ✅ 结构化数据提取
  • ✅ HTTP 客户端集成

🗑️ 移除的重复功能

  • ❌ 条件查询处理(Cheerio 原生支持)
  • ❌ 嵌套查询处理(Cheerio 原生支持)
  • ❌ 伪选择器重复实现(Cheerio 原生支持)

🤝 贡献

欢迎提交 Issue 和 Pull Request!

开发环境

# 克隆项目
git clone https://github.com/your-username/cparse.git

# 安装依赖
npm install

# 运行测试
npm test

# 运行 lint
npm run lint

📄 许可证

MIT License - 详见 LICENSE 文件

⭐ 如果这个项目对你有帮助,请给个 Star!

🔌 插件化架构与自定义集成

v2.1.0+ 新增功能cparse 引入了通用的插件创建工厂函数 createCheerioHook,允许你为任何 HTTP 客户端(或任何返回 HTML 的数据源)轻松创建集成插件。

cheerioHookForAxioscheerioHookForGot 内部就是使用这个工厂函数实现的。

createCheerioHook(options)

这个函数接受一个配置对象,并返回一个标准的钩子函数。

配置项 options:

  • name (string, 必需): 插件的名称,用于错误和警告信息 (例如: 'node-fetch')。
  • validate(instance) (function, 必需): 一个函数,用于验证传入的客户端实例是否有效。如果无效,应返回 false
  • attach(instance, hookFn) (function, 必需): 一个函数,负责将核心处理逻辑 (hookFn) 附加到客户端实例的生命周期钩子上(例如,在响应完成后执行)。
  • getBody(response) (function, 必需): 一个函数,告诉 cparse 如何从客户端的响应对象中提取 HTML 文本。
  • getUrl(response) (function, 必需): 一个函数,用于从响应对象中获取最终的请求 URL,这对于解析页面上的相对链接至关重要。

示例:为 node-fetch 创建集成插件

下面是一个完整的示例,展示了如何为 node-fetch(v3+)封装一个解析钩子。

const fetch = require('node-fetch');
const { createCheerioHook, parse } = require('cparse');

// 1. 使用 createCheerioHook 定义针对 node-fetch 的钩子
const cheerioHookForFetch = createCheerioHook({
  name: 'node-fetch',
  // 验证 fetch 函数本身(在这个例子中我们直接扩展 fetch)
  validate: (instance) => typeof instance === 'function',
  // getBody 和 getUrl 是异步的,所以 attach 也要是 async
  getBody: async (response) => await response.text(),
  getUrl: (response) => response.url,
  // 核心:替换原始的 fetch 函数
  attach: (originalFetch, hook) => {
    // 返回一个新的、被包装过的 fetch 函数
    return async (...args) => {
      const response = await originalFetch(...args);
      // 调用我们通用的处理逻辑
      // 注意:由于 getBody 是异步的,这里也需要 await
      return await hook(response);
    };
  },
});

// 2. 包装原始的 fetch 函数
const enhancedFetch = cheerioHookForFetch(fetch);

// 3. 使用增强后的 fetch
async function main() {
  const response = await enhancedFetch('https://example.com');
  const title = response.$.parse('title');
  console.log(title); // "Example Domain"
}

main();

通过这种方式,你可以将 cparse 的解析能力无缝集成到任何你选择的工具中。

Keywords

parse

FAQs

Package last updated on 17 Jun 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts