资讯专栏INFORMATION COLUMN

feedparser学习与实战——基于Node.js的Feed解析器

Half / 548人阅读

摘要:因为这里用到了异步编程的事件监听,所有的动作都是异步操作,如果通过上述两个办法取出的值都是。但需要注意,在调用的地方也需要异步编程编码问题在抓取非英文网页时,总会遇到编码问题,中文也不例外。

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

项目地址 / https://github.com/danmactoug...

目录

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 , feedburnerpheedo 这些扩展。举例:如果一个feed的article同时包含了 itunes:imagemedia:thumbnail ,那么这两个的url地址都会保存到article的 image.url 属性中。

所有的属性都会进行初始化,设置为 null (空数组或者空对象都会有恰当的属性)。这个能够节省你很多时间来检查属性是否为 undefined ,例如,当你使用jade模板的时候。

除此之外,所有的属性(包含命名空间)都使用小写字母("xmlUrl" and
"pubDate"仍然提供向下兼容)。“好用”取代了“原生”——衷心希望你能不必为骆驼拼写法而烦恼。

如果你设置normalize为true,那么 metaarticletitledescription 属性都会将HTML标签剥离。如果你需要这些HTML元素,你可以从 meta["atom:subtitle"]["#"] 这个属性取得。

meta属性列表

title

description

link (网站链接)

xmlurl

date (最近的日期)

pubdate (原始出版日期)

author

language

image (一个对象,包含 urltitle 属性)

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);

因为 requestfeedparser 之间的通讯是通过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() {
    // ...省略
  });

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86839.html

相关文章

  • 学习 | 朴素贝叶斯

    摘要:由于近期学业繁重,所以我就不说废话了,直接上代码朴素贝叶斯进行文本词汇分类词表到向量的转换创建实验样本,返回的是进行词条切分后的文档集合,还有一个类别标签侮辱性的非侮辱性的代表侮辱性文字代表正常言论创建一个包含在所有文档中出现的不重复的词的 由于近期学业繁重QAQ,所以我就不说废话了,直接上代码~ 朴素贝叶斯进行文本词汇分类 from numpy import * #词表到向量的转换...

    yuxue 评论0 收藏0
  • 前端每周清单年度总结盘点

    摘要:前端每周清单年度总结与盘点在过去的八个月中,我几乎只做了两件事,工作与整理前端每周清单。本文末尾我会附上清单线索来源与目前共期清单的地址,感谢每一位阅读鼓励过的朋友,希望你们能够继续支持未来的每周清单。 showImg(https://segmentfault.com/img/remote/1460000010890043); 前端每周清单年度总结与盘点 在过去的八个月中,我几乎只做了...

    jackwang 评论0 收藏0

发表评论

0条评论

Half

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<