node.js 如何解析 html 标签,实现 jquery 一样的元素选择操作
安装
npm i -D cheerio
解析html(load)
const cheerio = require('cheerio');
const $ = cheerio.load('<h2 class="title">hello world</h2>');
$('h2').addClass('welcome');
console.log($.html());
//=> <html><head></head><body><h2 class="title welcome">hello world</h2></body></html>
如果你需要修改默认的解析可以
const cheerio = require('cheerio');
const $ = cheerio.load('<h2 class="title">hello world</h2><ul></ul>', {
normalizeWhitespace: false,
xmlMode: false,
decodeEntities: true
});
$('h2').addClass('welcome');
console.log($.html());
这些解析选项直接取自 htmlparser2, 因此也可以在cheerio中使用任何在htmlparser2
中有效的选项。
options | 默认值 | 内容 |
xmlMode | false | 指示特殊标记(和<style>)是否应得到特殊处理,以及“空”标记(例如<br>)是否可以有子标记。如果为false,则特殊标记的内容将仅为文本。对于提要和其他XML内容(不包含HTML的文档),将其设置为true。 |
decodeEntities | true | 如果设置为true,文档中的实体将被解码。 |
lowerCaseTags | 如果设置为true,则所有标记都将小写。如果xmlMode被禁用,则默认为true。 | |
lowerCaseAttributeNames | false | 如果设置为true,则所有属性名称都将小写。这对速度有显著影响。 |
recognizeCDATA | 如果设置为true,即使未启用xmlMode选项,CDATA节也将被识别为文本。注意:如果xmlMode设置为true,则CDATA节将始终被识别为文本。 | |
recognizeSelfClosing | 如果设置为true,则即使xmlMode未设置为true,自动关闭标记也会触发onclosetag事件。注意:如果xmlMode设置为true,则将始终识别自动关闭标记。 |
const cheerio = require('cheerio');
const $ = cheerio.load(`<div id="cheerio">
<h2 class="title"><p>hello world</p></h2>
<ul id="list" class="web-list">
<li class="react">react.js</li>
<li class="vue">vue.js</li>
<li class="angular">angular.js</li>
</ul>
</div>`, {
normalizeWhitespace: false,
xmlMode: false,
decodeEntities: true
});
$('h2').addClass('welcome');
console.log($.html());
选择器
cheerio和jQuery选择器的实现几乎是相同的,所以API非常相似。
$( selector, [context], [root] )
selector
在 root
的范围内搜索 context
。selector
和context
可以是一个字符串表达式,DOM元素,DOM元素的数组,或cheerio对象。root
通常是HTML文档字符串。
这个选择器方法的出发点是遍历和操作文档。就像jQuery,它是选择文档中元素的主要方法,但不像jQuery它基于CSSSelect库来实现大部分的选择器。
$('.title', '#cheerio').text()
//=> hello world
$('ul.web-list').attr('class')
//=> web-list
$('li[class=react]').html()
// react.js
$('li[class=react]', '', '<li class="react">custom react.js</li>').html()
//=> custom react.js
属性
获取和修改属性的方法。
.attr( name, value )
获取和设置属性的方法。在匹配集合中只能获取的第一个元素的属性值。如果你把属性值设置为空,就会删除该属性。你也可以像jQuery函数一样通过传递键值
和 函数
来设置。
$('ul').attr('class')
//=> web-list
$('.react').attr('id', 'add-react-id')
//=> <li class="react" id="add-react-id">react.js</li>
.prop( name, value )
获取和设置属性的方法。获取唯一匹配集的第一元素的属性值。
$('input[type="checkbox"]').prop('checked')
//=> false
$('input[type="checkbox"]').prop('checked', true).val()
//=> ok
.data( name, value )
获取和设置数据属性的方法。获取或设置仅在匹配集合中的第一个元素的数据属性值。
$('<div data-default-color="red"></div>').data()
//=> { defaultColor: 'red' }
$('<div data-default-color="red"></div>').data('default-color')
//=> red
const node = $('.react').data('module', 'cheerio')
node.data('module')
//=> cheerio
.val( [value] )
获取和设置input, select, and textarea的值的方法。注:对于传递键值
,函数
的支持尚未实现。
$('input[type="text"]').val()
// 获取 input value
$('input[type="text"]').val('cheerio').html()
// 设置
.removeAttr( name )
通过name
属性移除属性
$('.react').removeAttr('class').html()
// <li>react.js</li>
.hasClass( className )
检查任何一个匹配的元素中是否有className
。
$('.web-list').hasClass('react')
//=> true
$('web-list').hasClass('node')
//=> false
$('ul').hasClass('web-list')
//=> true
.addClass( className )
在所有匹配的元素中添加类。也像jQuery函数一样接受函数
。
$('h2').addClass('welcome');
// <h2 class="title welcome"><p>hello world</p></h2>
.removeClass( [className] )
从选定的元素中删除一个或多个空格分隔的class。如果removeClass函数的className
是未定义的(即未传参数),所有的class值会被删除。也像jQuery函数一样接受函数
。
$('.web-list').removeClass('react').html()
// <li class="">react.js</li>
$('.web-list').addClass('box').removeClass().html()
// <ul class="">...</ul>
.toggleClass( className, [switch] )
从匹配的元素中添加或删除类,这取决于class的存在或切换参数的值。也像jQuery函数一样接受函数
。
$('.react.green').toggleClass('fruit green red').html()
//=> <li class="react fruit red">react.js</li>
$('.react.green').toggleClass('fruit green red', true).html()
//=> <li class="react green fruit red">react.js</li>
is( selector )
.is( element )
.is( selection )
.is( function(index) )
有任何元素匹配selector就返回true。如果使用判定函数,判定函数在选中的元素中执行,所以this指向当前的元素。
Forms
.serializeArray()
将一组表单元素编码为一组键值对。
$('<form><input name="name" value="zhangsan" /></form>').serializeArray()
//=> [ { name: 'name', value: 'zhangsan' } ]
Traversing
.find(selector)
.find(selection)
.find(node)
通过选择器、jQuery对象或元素来过滤,获取每个匹配元素的后代。
$('.web-list').find('li').length
//=> 3
$('.web-list').find($('.react')).length
//=> 1
.parent([selector])
获得每个匹配元素的parent,可选择性的通过selector筛选。
$('.react').parent().attr('id')
//=> list
.parents([selector])
获得通过选择器筛选匹配的元素的parent集合。
$('.react').parents().length
// => 1
$('.react').parents('#list').length
// => 1
.parentsUntil([selector][,filter])
获取从匹配元素到其指定的祖先元素之间的所有节点(不包括那个指定的祖先元素)。
$('.react').parentsUntil('#list').length
// => 1
.closest(selector)
获得离他最近的祖先元素(可以包括他自己,但不包括其兄弟节点)。
$('.react').closest()
// => []
$('.react').closest('.apple')
// => []
$('.react').closest('li')
// => [<li class="react">react.js</li>]
$('.react').closest('#list')
// => [<ul id="list"> ... </ul>]
.next([selector])
获取他的下一个兄弟节点。
$('.react').next().hasClass('vue')
//=> true
.nextAll([selector])
获取在他的后面的所有兄弟节点。
$('.react').nextAll()
//=> [<li class="vue">vue.js</li>, <li class="angular">angular.js</li>]
$('.react').nextAll('.vue')
//=> [<li class="vue">vue.js</li>]
.nextUntil([selector], [filter])
获取从他本身到指定节点之间的所有节点(不包括他本身和指定节点)。
$('.react').nextUntil('.vue')
//=> [<li class="vue">vue.js</li>]
.prev([selector])
获取选定元素的前一个兄弟节点,可以选择一个选择器筛选。
$('.vue').prev().hasClass('react')
//=> true
.prevAll([selector])
获取在指定元素的前面的所有兄弟节点。
$('.angular').prevAll()
//=> [<li class="react">react.js</li>, <li class="vue">vue.js</li>]
$('.angular').prevAll('.react')
//=> [<li class="react">react.js</li>]
.prevUntil([selector], [filter])
获得从他本身到指定节点之间的所有兄弟节点(不包括他本身和指定节点)。
$('.angular').prevUntil('.react')
//=> [<li class="react">react.js</li>]
.slice( start, [end] )
获取指定范围匹配的元素
$('li').slice(1).eq(0).text()
//=> 'react'
$('li').slice(1, 2).length
//=> 1
.siblings([selector])
获取选定元素的所有兄弟,不包括它自己(可以通过传入class改变筛选结果)。
$('.react').siblings().length
//=> 2
$('.react').siblings('.vue').length
//=> 1
.children([selector])
获取选定元素的孩子元素。
$('#list').children().length
//=> 1
$('#list').children('.vue').text()
//=> vue.js
.contents()
获取匹配元素集合中的每个元素的孩子元素,包括文本和注释节点。
$('#list').contents().length
//=> 3
.each( function(index, element) )
迭代一个cheerio对象,为每个匹配元素执行一个函数。当回调函数执行后,该函数所处的环境是DOM element,所以this
指向当前元素,这相当于函数的参数element
。要提前打破的each
循环,那么返回false
。
var fruits = [];
$('li').each(function(i, elem) {
fruits[i] = $(this).text();
});
fruits.join(', ');
//=> react.js, vue.js, angular.js
.map( function(index, element) )
通过每个在匹配函数产生的匹配集合中的匹配元素,产生一个新的包含返回值的cheerio对象。该函数可以返回一个单独的数据项或一组数据项被插入到所得到的集合中。如果返回一个数组,数组中的元素插入到集合中。如果函数返回空或未定义,则将插入任何元素。
$('li').map(function(i, el) {
// this === el
return $(this).text();
}).get().join(' ');
//=> "react.js vue.js angular.js"
.filter( selector )
.filter( selection )
.filter( element )
.filter( function(index) )
遍历一个cheerio对象,降低通过选择器匹配的元素,或传递函数的测试的选择器元素组。当一个cheerio的选择是特定的,只返回元素的选择。当指定元素时,返回该元素(如果它包含在原始选择中)。如果使用该函数方法,该函数将在选定的元素的环境中执行,所以this
是指当前元素。
Selector:
$('li').filter('.react').attr('class');
//=> react
Function:
$('li').filter(function(i, el) {
// this === el
return $(this).attr('class') === 'react';
}).attr('class')
//=> react
.not( selector )
.not( selection )
.not( element )
.not( function(index, elem) )
从匹配的元素集合中删除元素。给定一个jQuery对象表示一组DOM元素,.not()
方法从匹配的元素的子集构造了一种新的jQuery对象。所提供的选择器对每个元素进行了测试;结果中不匹配选择器的元素将被包含在该结果中。.not()
方法可以把一个函数作为参数就像.filter()
一样。将函数返回为真的元素从过滤的集合中排除,所有其他元素都包括在内。
选择器:
$('li').not('.react').length;
//=> 2
Function:
$('li').not(function(i, el) {
// this === el
return $(this).attr('class') === 'react';
}).length;
//=> 2
.has( selector )
.has( element )
匹配一个拥有指定孩子元素的父元素。相当于.filter(':has(selector)')
。
选择器:
$('ul').has('.react').attr('id');
//=> fruits
元素:
$('ul').has($('.react')[0]).attr('id');
//=> list
.first()
选择一个cheerio的对象的第一个元素
$('#list').children().first().text()
//=> react
.last()
选择一个cheerio对象的最后一个元素
$('#list').children().last().text()
//=> angular
.eq( i )
根据索引来确定元素。使用 .eq(-i)
的则是倒过来计数。
$('li').eq(0).text()
//=> react
$('li').eq(-1).text()
//=> vue
.get( [i] )
检索出匹配的cheerio对象的DOM元素。如果指定索引,检索出一个匹配的cheerio对象的元素:
$('li').get(0).tagName
//=> li
如果没有指定索引,检索出所有匹配的cheerio对象元素:
$('li').get().length
//=> 3
.index()
.index( selector )
.index( nodeOrSelection )
在匹配元素中搜索给定元素。
$('.angular').index()
//=> 2
$('.vue').index('li')
//=> 1
$('.vue').index($('#list, li'))
//=> 1
.end()
结束当前链中最新的过滤操作,将匹配的元素集合返回到它的前一状态。
$('li').eq(0).end().length
//=> 3
.add( selector [, context] )
.add( element )
.add( elements )
.add( html )
.add( selection )
在匹配元素集合中添加元素。
$('.react').add('.vue').length
//=> 2
.addBack( [filter] )
将堆栈上的前一组元素添加到当前集合中,选择一个选择器进行筛选。
$('li').eq(0).addBack('.orange').length
//=> 2
操作
修改DOM结构的方法。
.append( content, [content, …] )
插入内容为每个选定元素的 last 孩子。
$('ul').appednTo('<li class="node">node.js</li>')
$.html()
//=> <ul id="list">
// <li class="react">react.js</li>
// <li class="vue">vue.js</li>
// <li class="angular">angular.js</li>
// <li class="node">node.js</li>
// </ul>
.appendTo( target )
将每一个元素插入到目标的末端。
$('<li class="node">node.js</li>').appednTo('#list')
$.html()
//=> <ul id="list">
// <li class="react">react.js</li>
// <li class="vue">vue.js</li>
// <li class="angular">angular.js</li>
// <li class="node">node.js</li>
// </ul>
.prepend( content, [content, …] )
插入内容为每个选定元素的 first 孩子。
$('ul').prepend('<li class="plum"></li>')
$.html()
//=> <ul id="list">
// <li class="node">node.js</li>
// <li class="react">react.js</li>
// <li class="vue">vue.js</li>
// <li class="angular">angular.js</li>
// </ul>
.prependTo( target )
将每一个元素插入到目标的开始。
$('<li class="node">node.js</li>').prependTo('#list')
$.html()
//=> <ul id="list">
// <li class="node">node.js</li>
// <li class="react">react.js</li>
// <li class="vue">vue.js</li>
// <li class="angular">angular.js</li>
// </ul>
.after( content, [content, …] )
在匹配元素集合中的元素后面插入内容。
$('.angular').after('<li class="node">node.js</li>')
$.html()
//=> <ul id="list">
// <li class="react">react.js</li>
// <li class="vue">vue.js</li>
// <li class="node">node.js</li>
// <li class="angular">angular.js</li>
// </ul>
.insertAfter( target )
将每一个元素插入匹配元素集合中的目标后。
$('<li class="node">node.js</li>').insertAfter('.react')
$.html()
//=> <ul id="list">
// <li class="react">react.js</li>
// <li class="node">node.js</li>
// <li class="vue">vue.js</li>
// <li class="angular">angular.js</li>
// </ul>
.before( content, [content, …] )
在匹配元素集合的元素前插入元素。
$('.angular').before('<li class="node">node.js</li>')
$.html()
//=> <ul id="list">
// <li class="react">react.js</li>
// <li class="vue">vue.js</li>
// <li class="angular">angular.js</li>
// <li class="node">node.js</li>
// </ul>
.insertBefore( target )
在目标元素前插入元素。
$('<li class="node">node.js</li>').insertBefore('.react')
$.html()
//=> <ul id="list">
// <li class="node">node.js</li>
// <li class="react">react.js</li>
// <li class="vue">vue.js</li>
// <li class="angular">angular.js</li>
// </ul>
.remove( [selector] )
将匹配的元素集合从DOM和他们的孩子节点中删除。
$('.angular').remove()
$.html()
//=> <ul id="list">
// <li class="react">react.js</li>
// <li class="vue">vue.js</li>
// </ul>
.replaceWith( content )
将匹配的元素替换为 content
。
var node= $('<li class="node">node.js</li>')
$('.pear').replaceWith(plum)
$.html()
//=> <ul id="list">
// <li class="react">react.js</li>
// <li class="vue">vue.js</li>
// <li class="node">node.js</li>
// </ul>
.empty()
清空一个元素,去除所有的孩子。
$('ul').empty()
$.html()
//=> <ul id="list"></ul>
.html( [htmlString] )
获取第一个选中元素的HTML内容字符串。如果htmlstring
被指定,那么选中元素的内容被新内容所取代。
$('.react').html()
//=> react
$('#list').html('<li class="mango">Mango</li>').html()
//=> <li class="mango">Mango</li>
.text( [textString] )
获取元素集合中的每个元素的合并文本内容,包括它们的后代。如果 textString
指定文本字符串,每个元素的内容被新的内容替换。
$('.react').text()
//=> react
$('ul').text()
//=> react
// vue
// angular
.wrap( content )
.wrap()函数可以使用任何可以传递给$()工厂函数字符串或对象来指定DOM结构。这种结构可以嵌套多个层次,但应该只包含一个内心深处的元素。这一结构的副本将在匹配的元素集合中的每一个元素被包装。此方法返回的链接目标的原始元素集。
var redFruit = $('<div class="red-fruit"></div>')
$('.react').wrap(redFruit)
//=> <ul id="list">
// <div class="red-fruit">
// <li class="react">react.js</li>
// </div>
// <li class="vue">vue.js</li>
// <li class="react.js">angular.js</li>
// </ul>
var healthy = $('<div class="healthy"></div>')
$('li').wrap(healthy)
//=> <ul id="list">
// <div class="healthy">
// <li class="react">react.js</li>
// </div>
// <div class="healthy">
// <li class="vue">vue.js</li>
// </div>
// <div class="healthy">
// <li class="angular">angular.js</li>
// </div>
// </ul>
.css( [propertName] )
.css( [ propertyNames] )
.css( [propertyName], [value] )
.css( [propertName], [function] )
.css( [properties] )
得到一个匹配元素的style属性值或者设置匹配元素的css属性。
渲染
当你准备好要渲染的文件,你可以使用html
效用函数:
$.html()
//=> <ul id="list">
// <li class="react">react</li>
// <li class="vue">vue</li>
// <li class="angular">angular</li>
// </ul>
如果你想返回outerHTML,你可以使用 $.html(selector)
:
$.html('.react')
//=> <li class="react">react.js</li>
默认情况下,html
会留下一些开放标签。有时你可能会想要渲染一个有效的文档。例如,你可能会对下面的XML片段:
$ = cheerio.load('<media:thumbnail url="http://www.foo.com/keyframe.jpg" width="75" height="50" time="12:05:01.123"/>');
…后来要渲染到XML。要做到这一点,你可以使用 ‘xml’ 效用函数:
$.xml()
//=> <media:thumbnail url="http://www.foo.com/keyframe.jpg" width="75" height="50" time="12:05:01.123"/>
其他
DOM元素的方法不适合的地方
.toArray()
检索jQuery中所有的DOM元素然后设置为一个数组。
$('li').toArray()
//=> [ {...}, {...}, {...} ]
.clone()
克隆cheerio对象
var moreList = $('#list').clone()