node-feedparser
这篇文章是我学习node-feedparser的时候所写的,前半部分是翻译了node-feedparser在github上的原文(英语不好,如果翻译有误,还请见谅。),当中也夹杂了一些自己解释;后半部分的实战是我结合 compressed.js
,同时自己在实际运用中的小结,现在拿出来和大家分享,希望大家给予指正。
文章信息
时间 / 2017年3月9日
-
版本号
Node.js: v6.10.0
node-feedparser: v2.1.0
request: v2.79.0
iconv-lite: v0.4.15
目录
Feedparser 基于Node.js的RSS,Atom,RDF强劲解析器
如何安装
-
用法
feedparser可选参数
例子
API
-
feedparser解析后能得到什么?
meta属性列表
article属性列表
贡献
License
-
实战
处理数据
取得数据
编码问题
错误捕捉
Feedparser 基于Node.js的RSS,Atom,RDF强劲解析器
Feedparser是一个基于基于Node.js的强劲解析器,可以解析包括RSS,Atom和RDF信息
它有一对特性是你在其他的Feed解析器中不常见的:
它可以解析一些相对URL链接(例如Tim Bray's "ongoing"这个Feed)
它可以正确地解析一些XML命名空间(包含那些非常规的Feed——用主要的一些Feed元素来定义的非常规命名空间)
说明:对第二条的理解是,通常Feed的XML命名是固定的一些标签,但是Feedparser同样可以对一些非常规的XML进行解析,这就是强大之处。
如何安装
npm install feedparser
用法
这个例子能简要地示范feedparser的基本概念:
请注意在学习基本的示范的同时,也要学习简化的范例compressed.js文件,这样也能够让你更全面地开展工作。
var FeedParser = require('feedparser');
var request = require('request'); // 需要引入一个request,用于抓取Feed
var req = request('http://somefeedurl.xml')
var feedparser = new FeedParser([options]);
req.on('error', function (error) {
// 解决任何的request请求错误
// 这个是request包的错误提示
});
req.on('response', function (res) {
var stream = this; // 这里的this是req(所请求request文件),是stream文件类型
if (res.statusCode !== 200) {
this.emit('error', new Error('Bad status code'));
}
else {
stream.pipe(feedparser);
}
});
feedparser.on('error', function (error) {
// 处理feedparser的错误
// 这个是feedparser包的错误提示
});
feedparser.on('readable', function () {
// 此时已经获取到Feed信息,在这里可以进行你的操作了
var stream = this; // 这里的this是feedparser, 也是stream文件类型
var meta = this.meta; // 注意:这个meta是在feedparser的实例中一直可以获得。
//Meta其实是RSS等一些的元信息,在后面会介绍到,一般来说Meta信息都是重复的。
var item;
while (item = stream.read()) {
console.log(item);
//在这里输出每一条Feed信息。
}
});
feedparser可选参数
normalize
:设置false
让Feedparser的默认值失效。无论这个Feed的形式,这个参数都能将其解析成一个包含RSS2.0格式,正确属性的对象。序列化后的形式如以下所示(只进入第一层):
//通过bash输出:
属性名:rss:@ 值:[object Object]
属性名:rss:title 值:[object Object]
属性名:rss:link 值:[object Object]
属性名:rss:author 值:[object Object]
属性名:rss:guid 值:[object Object]
属性名:rss:category 值:[object Object]
属性名:rss:pubdate 值:[object Object]
属性名:rss:comments 值:[object Object]
属性名:rss:description 值:[object Object]
属性名:meta 值:[object Object]
addmeta
:设置false
让Feedparser的默认值失效。让每一个Feed的article信息都加入Meta信息。个人建议可以设置为true,一般来说,每个Feed的Meta信息都是唯一的,而article信息不同,只要第一次获取Meta后,就可以只需要article信息。feedurl
:Feed的URL地址。FeedParser能非常优秀地处理相对url,但是一些Feed在使用相对url时,并不声明xml:base
信息。尽管feedparser非常有效,但是在我们处理feed和尝试着处理这些相对url之前,我们仍然不能知道feed的url。如果我们发现了feed的url,我们将会返回并处理那些我们已经得到的相对url,但这将会消耗一段时间(并非很长)。如果你想要确信我们不对相对url进行预处理(或者feedparser无法处理相对url),你应该设置feedurl
选项,否则,你就当没看见过它吧~resume_saxerror
:设置false
让Feedparser的默认值失效。这个选项能够抛出error
中的SAXError
错误,并自动进行后续的解析。在我的测试中,SAXErrors
并不常错误,所以开启这个选项通常对你很有帮助。如果你想要完全掌握和处理错误,并在任意点中止解析feed的话,可以尝试用这个选项。
例子
在这里查看例子 examples
。
API
转换 Stream
Feedparser是一个stream转换器(关于stream你可以在nodejs官网阅读),从XML文件转换为Javascript的objects对象。
每一个可读的区块都是一个对象,这个对象代表feed中的article信息。
发出的项目
meta
- 被解析后,称作feed的meta
error
- 任何时候Feedparser发出的错误(包括SAXError, Feedparser error等)
feedparser解析后能得到什么?
Feedparser对每一个Feed都会解析出 meta
和一个或更多的 articles
。
不论Feed的形式如何, meta
和每一个 article
都包含一个RSS2.0规范同时加上规范化后的属性的信息流。举个例子,一个Atom feed会有一个 meta.description
属性,但是同样会有一个 meta['atom:subtitle']
属性。
这个规范化后的属性是用于向用户提供一个规范的接口——当你不知道feed的形式,或者搞不清不同feed形式之间的差异时,用这个接口就可以方便获取feed信息。不过当你需要原始信息的时候会依然会保留给你使用。此外,Feedparser还提供了一些大众化的命名空间扩展,例如 itunes
, media
, feedburner
和 pheedo
这些扩展。举例:如果一个feed的article同时包含了 itunes:image
或 media:thumbnail
,那么这两个的url地址都会保存到article的 image.url
属性中。
所有的属性都会进行初始化,设置为 null
(空数组或者空对象都会有恰当的属性)。这个能够节省你很多时间来检查属性是否为 undefined
,例如,当你使用jade模板的时候。
除此之外,所有的属性(包含命名空间)都使用小写字母("xmlUrl" and
"pubDate"仍然提供向下兼容)。“好用”取代了“原生”——衷心希望你能不必为骆驼拼写法而烦恼。
如果你设置normalize为true,那么 meta
和 article
的 title
和 description
属性都会将HTML标签剥离。如果你需要这些HTML元素,你可以从 meta['atom:subtitle']['#']
这个属性取得。
meta属性列表
title
description
link (网站链接)
xmlurl
date (最近的日期)
pubdate (原始出版日期)
author
language
image (一个对象,包含
url
和title
属性)favicon (favicon的链接——只提供给Atom feeds)
copyright
generator
categories (一个字符串数组)
article属性列表
title
description (通常是完整的标题内容)
summary (通常是文章摘录)
link
origlink
permalink
date
pubdate
author
guid
comments
image
categories
source
enclosures
meta
贡献
在这里查看所有代码 -> contributors。
License
(The MIT License)
实战
处理数据
feedparser.on('readable', function() {
var item;
while (item = this.read()) {
//一般我们在这里获取数据,在上面提到的,feedparser一共会输出两种信息,一种是规范化后的RSS2.0,另一种是原有的。
//原有信息的获取:(推荐这种)
console.log(item.meta.title);
console.log(item.title);
//RSS2.0信息的获取:
console.log(item['meta']['rss:title']['#']);
console.log(item['rss:title']['#']);
}
});
需要注意的是你无法通过return将上述的数据从函数中取出,也无法通过在外定义变量,在内赋值取出。因为这里用到了异步编程的事件监听,所有的动作都是异步操作,如果通过上述两个办法取出的值都是undefined。那么如何取出这些数据?这里有两个办法:
取得数据
因为feedparser使用的是异步编程的办法,所以无法通过常规方法取出值,不过仍然有以下两种办法:
直接在函数中进行操作
使用Promise封装
/*
* 在函数中直接进行操作不再演示
* 这里主要演示Promise封装
*/
new Promise((resolve, reject)=>{
//这里是一些request操作代码,暂时省略
feedparser.on('readable', function() {
var item;
while (item = this.read()) {
resolve(item);
}
}).then((result)=>{
//在这里可以用then继续操作
console.log(result.title);
//也可以return一个Promise对象,并在其他地方调用这个Promise。
//但需要注意,在调用Promise的地方也需要异步编程
return result;
}).catch((err)=>{
console.log(err);
});
编码问题
在抓取非英文网页时,总会遇到编码问题,中文也不例外。比如新浪新闻的编码是"utf-8",但是腾讯新闻的编码是"gb3212"。feedparser虽然强大,但不负责解决这些问题,这个时候需要我们引入 iconv-lite
,来解决编码问题。
var url = "http://www.example.xml";
var req = request(url);
var feedparser = FeedParser();
var encode = 'utf-8';
req.on('response', function (res) {
console.log(res.statusCode); // 200
console.log(res.headers['content-type']); // 'image/png'
}).pipe(iconv.decodeStream(encode)) //在iconv-lite可以直接调用
.pipe(feedparser);
因为 request
和 feedparser
之间的通讯是通过stream流的,而 iconv-lite
正好又有对于Stream流的API接口,所以直接调用即可。如果感兴趣或者有需要的同学还可以去查看 iconv-lite
的官方文档查看其它的方法。
错误捕捉
在整个抓取Feed的过程中,会遇到很多的错误,如何处理这些错误?这里借鉴一下官方文档所提到的 compressed.js
中的方法,它将所有的错误处理写成一个函数,然后在每次事件监听的地方,调用处理错误函数即可完成。
//错误处理函数:
function done(err) {
if (err) {
console.log(err, err.stack);
return process.exit(1);
}
process.exit();
}
//事件监听调用done
// ...省略
req.on('error', done);
// ...省略
feedparser.on('error', done);
feedparser.on('end', done);
feedparser.on('readable', function() {
// ...省略
});