Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台, 用来方便地搭建快速的, 易于扩展的网络应用· Node.js 借助事件驱动, 非阻塞 I/O 模型变得轻量和高效, 非常适合 run across distributed devices 的 data-intensive 的实时应用·
提供RSS服务的站点超级多,百度、网易、新浪、虎嗅网 等等站点,基于java c++ php的rss抓取网上很多,今天说说NodeJs抓取RSS信息,
使用NodeJs做网络爬虫,抓取RSS新闻。各站点编码格式不一样 GBK,UTF-8,ISO8859-1等等,所以需要进行编码,对国人来说UTF-8是最酷的。抓取多站点,然后保存到数据库,充分利用javascript异步编程的特点,抓取速度超级快呀。
这个项目是为新闻android客户端实现的,以后我也会上传新闻客户端的源码。
本项目的源码托管在github:https://github.com/kissliux/rssSpider
环境需求:
NodeJs(必须), 我的版本是0.10.24
mongodb(可选),或者mysql等等其他数据库
编程工具:webStrom
"dependencies": {
"express": "3.4.8",
"ejs": "*",
"feedparser":"0.16.6",
"request":"2.33.0",
"iconv":"2.0.7",
"mongoose":"3.8.7",
"mongodb":"*"
}
npm install -d
基本准备工作完毕,可以动手了写代码了。RSS抓取,主要依赖于feedparser 库,github地址:http://github.com/danmactough/node-feedparser
先配置下,需要抓取的站点信息。
建立一个rssSite.json文件
{
"channel":[
{
"from":"baidu",
"name":"civilnews",
"work":false, //false 则不抓取
"title":"百度国内最新新闻",
"link":"http://news.baidu.com/n?cmd=4&class=civilnews&tn=rss",
"typeId":1
},{
"from":"netEase",
"name":"rss_gn",
"title":"网易最新新闻",
"link":"http://news.163.com/special/00011K6L/rss_gn.xml",
"typeId":2
}
]
}
我要抓取的就是这两个站点,channel的值是一个对象数组,如果你需要多个站点,直接添加就行了。
引入相关的包,
var request = require('request')
, FeedParser = require('feedparser')
, rssSite = require('../config/rssSite.json')
, Iconv = require('iconv').Iconv;
var channels = rssSite.channel;
channels.forEach(function(e,i){
if(e.work != false){
console.log("begin:"+ e.title);
fetch(e.link,e.typeId);
}
});
work为false的站点,都不进行抓取。即黑名单吧,typeId是标识这个新闻是属于哪个栏目,社会,财经还是其他。
关键在于fetch函数,抓取和分析都在这里了。我先贴代码 再来解释
function fetch(feed,typeId) {
var posts;
// Define our streams
var req = request(feed, {timeout: 10000, pool: false});
req.setMaxListeners(50);
// Some feeds do not response without user-agent and accept headers.
req.setHeader('user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36')
.setHeader('accept', 'text/html,application/xhtml+xml');
var feedparser = new FeedParser();
// Define our handlers
req.on('error', done);
req.on('response', function(res) {
var stream = this
, iconv
, charset;
posts = new Array();
if (res.statusCode != 200) return this.emit('error', new Error('Bad status code'));
charset = getParams(res.headers['content-type'] || '').charset;
if (!iconv && charset && !/utf-*8/i.test(charset)) {
try {
iconv = new Iconv(charset, 'utf-8');
iconv.on('error', done);
stream = this.pipe(iconv);
} catch(err) {
this.emit('error', err);
}
}
stream.pipe(feedparser);
});
feedparser.on('error', done);
feedparser.on('end', function(err){
// postService.savePost(posts); //存到数据库
});
feedparser.on('readable', function() {
var post;
while (post = this.read()) {
posts.push(transToPost(post));//保存到对象数组
}
});
function transToPost(post){
var mPost = new Post({
title : post.title,
link : post.link,
description : post.description,
pubDate : post.pubDate,
source : post.source,
author : post.author,
typeId : typeId
});
return mPost;
}
}
1、关键函数: request(url,[option]); 这个是可以发送http请求的函数。 地址: http://github.com/mikeal/request.git
var req = request(feed, {timeout: 10000, pool: false});
req 需要监听几个状态 response,error。请求发出后,会收到响应,有响应后,把接收到的数据进行拼接,拼接前需要进行编码转换。把非utf8的编码转换成utf8.这里利用到了库 ICONV。地址:http://github.com/bnoordhuis/node-iconv
res.headers['content-type'].charset;
这样获取你抓取站点的编码格式。
拼接前需要进行编码转换,当然这里用了高明点的作法,pipe管道
然后进行转换成我没可以操作的对象,这个时候需要feedparser出马了,
var feedparser = new FeedParser();
feedparse也监听几个状态 readable,end,error
readable的回调方法 一次会读到一条记录,每次读到一条记录 我就存到数组对象中。
所有数据读完会调用 end的回调函数。到此,站点抓取完成了。 还是多站点呀
数据全部存在了posts 的数组对象中,要怎么处理,任凭您发落了。存到mongoDB也就几行代码的事情。这里用到了mongoose库
当然mongodb库也是必须要的。 mongoose类似于baseDao的感觉 操作mongodb数据库非常方便。
先建立模式:
var mongoose = require('mongoose');
var PostSchema = new mongoose.Schema({
title:String,
link :String,
description :String,
pubDate :String,
source :String,
author :String,
typeId : Number
});
module.exports = PostSchema;
var mongoose = require('mongoose');
var PostSchema = require('../schemas/PostSchema');
var Post = mongoose.model('Post',PostSchema);
module.exports = Post;
var Post = require('../model/Post');
function savePost(posts){
for(var i = 0 ;i
到此新闻抓取结束,欢迎拍砖。
下一篇文章我会介绍“新闻http服务,mongodb分页实现”,当然继续使用nodejs