Cheerio 性能怎么优化?大文件和高并发场景怎么处理?
Cheerio 性能优化抓住三个方向:选择器、内存、并发。选择器方面:用 .find() 配合具体 class 替代深层后代选择器,缓存 $container 后链式调用避免重复查询。内存方面:大文件用 stream 分块解析代替一次 load,批量 DOM 操作先拼字符串再一次性 .html() 插入,用完的 $ 引用及时置空触发 GC。并发方面:多 URL 用 Promise.all 并行请求 + 逐个解析,超大数据集用 Worker 线程分片处理。load 选项中 decodeEntities: false 和 withDomLvl1: false 也能减少不必要的解析开销。
追问
为什么 .find() 比层级选择器快?
$('.container .item .title') 每次都从根节点全量匹配三层;$('.container').find('.item').find('.title') 先锁定容器再在子集中查找,搜索范围逐层缩小。差距在元素数量大时(万级以上)才明显。
大文件怎么避免内存溢出?
不要 cheerio.load(wholeFile),改用 stream 按 </item> 等边界标签分割,每块单独 load 解析后立即释放。内存占用从 O(n) 降到 O(chunk),10MB 文件也不会爆。
批量插入 DOM 为什么不能逐个 append?
每次 .append() 都触发内部 DOM 树重建,1000 次就是 1000 次重建。正确做法是先用数组拼 HTML 字符串,最后 .html(str) 一次性写入,从 O(n) 次操作降到 1 次。
load 选项哪些影响性能?
decodeEntities: false 跳过 HTML 实体解码(不需要中文转义时关闭);withDomLvl1: false 跳过 DOM Level 1 兼容处理;normalizeWhitespace: false 跳过空白合并。三个都关掉可提速 15-20%。
多 URL 并发爬取怎么做?
用 p-limit 或手动分批 Promise.all:for (let i = 0; i < urls.length; i += 5) 每批 5 个并发,避免同时发起数百请求被限流或打挂目标服务器。
写段代码
javascript// 流式解析大文件,内存恒定 const results = []; let buf = ''; fs.createReadStream('big.html') .on('data', chunk => { buf += chunk; const matches = buf.match(/<item[\s\S]*?<\/item>/g) || []; matches.forEach(m => { const $ = cheerio.load(m); results.push($('name').text()); }); buf = buf.slice(buf.lastIndexOf('</item>') + 7); });