cheerio
cheerio 是 jquery 核心功能的一个快速灵活而又简洁的实现,主要是为了用在服务器端需要对 DOM 进行操作的地方
简介
cheerio 是 nodejs 的抓取页面模块,为服务器特别定制的,快速、灵活、实施的 jQuery 核心实现。适合各种 Web 爬虫程序。
让你在服务器端和 html 愉快的玩耍
1 | var cheerio = require('cheerio'), |
安装
1 | npm install cheerio |
特点
- 熟悉的语法*:cheerio 实现了 jQuery 的一个子集,去掉了 jQuery 中所有与 DOM 不一致或者是用来填浏览器的坑的东西,重现了 jQuery 最美妙的 API
- 快到没朋友:cheerio 使用了及其简洁而又标准的 DOM 模型, 因此对文档的转换,操作,渲染都极其的高效。基本的端到端测试显示它的速度至少是 JSDOM 的 8 倍
- 极其灵活:cheerio 使用了@FB55编写的非常兼容的htmlparser2,因此它可以解析几乎所有的 HTML 和 XML
关于 JSDOM
cheerio 产生的原因是出于对 JSDOM 的失望,主要体现在以下三点:
- JSDOM 的解析规则太过于严格:JSDOM 的解析器无法处理现在许多的流行网站的内容
- JSDOM 太慢了:解析大的网站甚至可以产生可察觉的延迟
- JSDOM 太过于重量级:JSDOM 的目标是提供与浏览器一样的 DOM 环境,但是我们往往不需要这样。我们需要的只是一种简单,熟悉的方式来操作我们的 HTML
什么时候你应该用 JSDOM
cheerio 并非万能,当你需要一个浏览器一样的环境时,你最好还是用 JSDOM,尤其是你需要进行自动化的功能测试时
API
后面的例子中用到的 HTML 模板如下:
1 | <ul id="fruits"> |
解析 html(load)
首先你需要先加载你的 HTML。jQuery 会自动完成这一步,因为 jQuery 操作的 DOM 是固定的。但是在使用 cheerio 时我们要手动加载我们的 HTML 文档
首选的方式如下:
1 | var cheerio = require('cheerio'), |
其次,直接把 HTML 字符串作为上下文也是可以的:
1 | $ = require('cheerio'); |
或者把 HTML 字符串作为 root
1 | $ = require('cheerio'); |
如果你需要自定义一些解析选项,你可以多传递一个对象给 load 方法:
1 | $ = cheerio.load('<ul id = "fruits">...</ul>', { |
更多的解析选项可以参考domhandler和parser-options
选择器(selectors)
cheerio 的选择器几乎和 jQuery 一模一样,所以语法上十分相像
1 | $( selector, [context], [root] ) |
selector在context的范围内搜索,context的范围又包含在root的范围内。selector和context可以是一个字符串,DOM 元素,DOM 数组或者 cheerio 实例。root一般是一个 HTML 文档字符串
选择器是文档遍历和操作的起点。如同在 jQuery 中一样,它是选择元素节点最重要的方法,但是在 jQuery 中选择器建立在 CSS 选择器标准库上。cheerio 的选择器实现了大部分的方法
1 | $('.apple', '#fruits').text() |
属性操作(atrributes)
用来获取和更改属性的方法:
.attr(name, value)
这个方法用来获取和设置属性。获取第一个符合匹配的元素的属性值。如果某个属性值被设置成 null,那么该属性会被移除。你也可以把map和function作为参数传递进去,就像在 jQuery 中一样
1 | $('ul').attr('id') |
更多信息请查看 http://api.jquery.com/attr/
.removeAtrr(name)
移除名为 name 的属性
1 | $('.pear').removeAttr('class').html() |
.hasClass(className)
检查元素是否含有此类名
1 | $('.pear').hasClass('pear') |
.addClass(className)
添加类名到所有的匹配元素,可以用函数作为参数
1 | $('.pear').addClass('fruit').html() |
.remoteClass([className])
移除一个或者多个(空格分隔)的类名,如果 className 为空,则所有的类名都会被移除,可以传递函数作为参数
1 | $('.pear').removeClass('pear').html() |
遍历
.find(selector)
在当前元素集合中选择符合选择器规则的元素集合
1 | $('#fruits').find('li').length |
.parent()
获取元素集合第一个元素的父元素
1 | $('.pear').parent().attr('id') |
.next()
选择当前元素的下一个兄弟元素
1 | $('.apple').next().hasClass('orange') |
.prev()
同**.next()**相反
.siblings()
获取元素集合中第一个元素的所有兄弟元素,不包含它自己
1 | $('.pear').siblings().length |
.children( selector )
.each( function(index, element) )
遍历函数返回 false 即可终止遍历
1 | var fruits = []; |
.map( function(index, element) )
1 | $('li').map(function(i, el) { |
.filter( selector )
1 | $('li').filter('.orange').attr('class'); |
.filter( function(index) )
1 | $('li').filter(function(i, el) { |
.first()
1 | $('#fruits').children().first().text() |
.last()
1 | $('#fruits').children().last().text() |
.eq( i )
缩小元素集合,可以用负数表示倒数第 i 个元素被保留
1 | $('li').eq(0).text() |
操作 DOM
操作 DOM 结构的方法
.append( content, [content, …] )
.prepend( content, [content, …] )
.after( content, [content, …] )
1 | $('.apple').after('<li class = "plum">Plum</li>') |
.before( content, [content, …] )
1 | $('.apple').before('<li class = "plum">Plum</li>') |
.remove( [selector] )
1 | $('.pear').remove() |
.replaceWith( content )
1 | var plum = $('<li class = "plum">Plum</li>') |
.empty()
1 | $('ul').empty() |
.html( [htmlString] )
1 | $('.orange').html() |
.text( [textString] )
1 | $('.orange').text() |
解析和渲染
1 | $.html() |
输出包含自己在内的 HTML(outer HTML)
1 | $.html('.pear') |
杂项
.toArray()
1 | $('li').toArray() |
.clone()
1 | var moreFruit = $('#fruits').clone() |
常用工具
$.root()
1 | $.root().append('<ul id="vegetables"></ul>').html(); |
$.contains( container, contained )