文章
Cheerio 使用技巧总结
目录
🎯 核心概念#
1. 加载 HTML#
const cheerio = require('cheerio');
const $ = cheerio.load(html, {
decodeEntities: false, // 是否解码实体,默认为 true
xmlMode: false, // XML 模式,默认为 false
});
2. 选择器类型#
// CSS 选择器
$('div.class') // 类选择器
$('div#id') // ID 选择器
$('div[data-value]') // 属性选择器
$('parent > child') // 子选择器
$('div:first-child') // 伪类选择器
// 自定义筛选
$('div').filter((i, el) => $(el).text().includes('文本'))
$('div').find('span') // 后代查找
$('div').children() // 直接子元素
🔍 精准查找技巧#
1. 避免文本匹配污染#
// ❌ 错误:可能匹配到父元素
$("div").filter((_, el) => $(el).text().trim() === "目标文本")
// ✅ 正确:确保是直接文本
$("div").filter((_, el) => {
const $el = $(el);
// 检查元素是否直接包含文本(没有子元素)
return $el.children().length === 0 && $el.text().trim() === "目标文本";
})
// ✅ 使用 :contains 配合检查
$("div:contains('目标文本')").filter((_, el) => {
return $(el).children().length === 0;
})
2. DOM 遍历方法#
const $element = $('.target');
// 向上查找
$element.parent() // 直接父元素
$element.parents('.container') // 所有祖先元素中匹配选择器的
$element.closest('.wrapper') // 最近的匹配祖先
// 向下查找
$element.children('.child') // 直接子元素
$element.find('.descendant') // 所有后代元素
// 同级查找
$element.next() // 下一个兄弟
$element.nextAll('.sibling') // 后面所有兄弟
$element.prev() // 上一个兄弟
$element.prevAll('.sibling') // 前面所有兄弟
$element.siblings() // 所有兄弟元素
🛠 数据提取技巧#
1. 属性提取#
// 获取属性
const href = $('a').attr('href');
const data = $('div').data('value'); // data-value 属性
// 获取所有属性
const attrs = $('div').attr();
Object.keys(attrs).forEach(key => {
if (key.startsWith('data-')) {
console.log(`数据属性 ${key}: ${attrs[key]}`);
}
});
// 获取多个元素的属性
const links = $('a').map((i, el) => $(el).attr('href')).get();
2. 文本提取#
// 获取元素文本
const text = $('div').text(); // 包含子元素文本
const ownText = $('div').contents() // 只获取直接文本
.filter((_, node) => node.type === 'text')
.text();
// 获取多个元素文本
const texts = $('li').map((i, el) => $(el).text().trim()).get();
// 获取 HTML
const html = $('div').html(); // 内部 HTML
const outerHtml = $.html($('div')); // 外层 HTML
🎨 操作 DOM#
1. 修改内容#
// 设置内容
$('div').text('新文本'); // 设置文本
$('div').html('<span>新HTML</span>'); // 设置HTML
// 添加内容
$('div').append('<span>追加</span>'); // 内部末尾
$('div').prepend('<span>前插</span>'); // 内部开头
$('div').after('<div>之后</div>'); // 元素之后
$('div').before('<div>之前</div>'); // 元素之前
// 替换元素
$('div').replaceWith('<p>替换</p>');
2. 操作类和属性#
// 类操作
$('div').addClass('new-class');
$('div').removeClass('old-class');
$('div').toggleClass('active');
$('div').hasClass('target'); // 返回布尔值
// 属性操作
$('div').attr('title', '新标题'); // 设置
$('div').removeAttr('data-old'); // 删除
🧩 高级技巧#
1. 处理动态内容(常见问题)#
// 检查是否为空元素(可能动态加载)
const $content = $('.dynamic-content');
if ($content.length > 0 && $content.html().trim() === '') {
console.log('内容可能是动态加载的');
// 查找可能的脚本数据
const scripts = $('script').filter((_, el) => {
const text = $(el).html();
return text && text.includes('JSON') || text.includes('data');
});
scripts.each((i, el) => {
const scriptText = $(el).html();
// 尝试提取 JSON
const jsonMatch = scriptText.match(/\\{[\\s\\S]*\\}/);
if (jsonMatch) {
try {
const data = JSON.parse(jsonMatch[0]);
console.log('找到JSON数据:', data);
} catch(e) {
console.log('JSON解析失败');
}
}
});
}
2. 提取表格数据#
const tableData = [];
$('table tr').each((rowIndex, row) => {
const rowData = [];
$(row).find('td, th').each((cellIndex, cell) => {
// 处理合并单元格等情况
const colspan = parseInt($(cell).attr('colspan') || '1');
const rowspan = parseInt($(cell).attr('rowspan') || '1');
const text = $(cell).text().trim();
rowData.push({
text,
colspan,
rowspan,
row: rowIndex,
col: cellIndex
});
});
if (rowData.length > 0) {
tableData.push(rowData);
}
});
3. 链式操作优化#
// 避免重复查找
const $items = $('.list-item');
// ❌ 错误:多次查找
const first = $('.list-item').first();
const last = $('.list-item').last();
// ✅ 正确:链式操作
const $items = $('.list-item');
const first = $items.first();
const last = $items.last();
// ✅ 高效遍历
const results = $('.item')
.map((i, el) => {
const $el = $(el);
return {
title: $el.find('.title').text().trim(),
link: $el.find('a').attr('href'),
desc: $el.find('.desc').text().trim()
};
})
.get(); // .get() 将 cheerio 对象转为数组
🐛 调试技巧#
1. 查看元素结构#
// 查看元素及子元素
console.log('HTML:', $.html($element));
console.log('Outer HTML:', $element.toString());
console.log('Text content:', $element.text());
// 查看所有属性
console.log('Attributes:', $element.attr());
// 查看父级结构
let $parent = $element;
for (let i = 0; i < 3 && $parent.length; i++) {
console.log(`Parent ${i}:`, $parent.prop('tagName'), $parent.attr('class'));
$parent = $parent.parent();
}
2. 选择性打印#
// 打印前几个元素
$('div').slice(0, 5).each((i, el) => {
console.log(`Element ${i}:`, $(el).html());
});
// 打印特定属性的元素
$('*').each((i, el) => {
const $el = $(el);
if ($el.attr('data-src')) {
console.log(`Found data-src:`, $el.attr('data-src'));
}
});
📦 实用工具函数#
1. 清理和格式化#
function cleanText(text) {
return text
.replace(/\\s+/g, ' ') // 多个空格合并为一个
.replace(/\\n\\s*\\n/g, '\\n') // 多个空行合并为一个
.trim();
}
function extractNumber(text) {
const match = text.match(/\\d+(\\.\\d+)?/);
return match ? parseFloat(match[0]) : null;
}
2. 安全获取#
function safeGet($element, selector, defaultValue = '') {
const $found = $element.find(selector);
return $found.length ? $found.text().trim() : defaultValue;
}
function safeAttr($element, selector, attr, defaultValue = '') {
const $found = $element.find(selector);
return $found.length ? $found.attr(attr) || defaultValue : defaultValue;
}
⚠️ 常见陷阱与解决方案#
1. 空元素检查#
// ❌ 错误:只检查长度
if ($('.item').length) {
// .item 可能存在但内容为空
}
// ✅ 正确:检查内容和长度
const $items = $('.item');
if ($items.length && $items.text().trim()) {
// 确实有内容
}
2. 动态类名处理#
// 当类名包含动态哈希时(如 class_zJFy1)
const $elements = $('div[class*="container_"]'); // 部分匹配
const $elements = $('div[class^="container"]'); // 开头匹配
const $elements = $('div[class$="container"]'); // 结尾匹配
3. Cheerio vs jQuery 差异#
- 无 DOM 环境:Cheerio 没有浏览器环境,不能执行 JavaScript
- 无事件系统:不能绑定或触发事件
- 有限的伪类支持:部分 jQuery 伪类不可用
- 性能更好:比 jQuery 更轻量,适合服务器端
🚀 性能优化#
// 1. 限制查找范围
const $container = $('#main-content');
$container.find('.item'); // 只在容器内查找
// 2. 使用更具体的选择器
$('div.list > ul.items > li.item'); // 比 $('.item') 更快
// 3. 缓存结果
const $links = $('a'); // 缓存后重复使用
// 4. 减少 DOM 操作
// ❌ 不好:频繁操作
$('.item').each((i, el) => {
$(el).addClass('processed');
});
// ✅ 更好:批量操作
$('.item').addClass('processed');
Cheerio 是一个非常强大的 HTML 解析库,掌握这些技巧可以让你更高效地处理各种网页抓取和解析任务。记住,如果页面内容是通过 JavaScript 动态加载的,可能需要考虑使用 Puppeteer 或 Playwright 等工具。