此前在做项目的时候,一直用json文件用作模拟数据,后来发现了mock.js,于是就用了mock.js,再后来感觉这些数据再怎么模拟都是静态数据。所以就想用nodejs实现一个数据转发功能,在本地拉取服务端的数据。那时就简易做出了一个针对那个项目的数据拉取功能。而在最近,在看一些博客的时候,想把几个博客的页面内容全部拉取到一个页面来看。所以就把此前数据拉取功能稍作改造封装了一下。

  做出的一个简易数据拉取demo:点我看效果

  

  然后大概简述一下demo的实现。当作学习记录。

  首先是数据转发模块,我将其封装了一下,封装成了transdata.js

"use strict";

var http = require("http");
var stream = require("stream");
var url = require("url");
var zlib = require("zlib"); var noop = function () {}; //两种请求
var transdata = {
post: function (opt) {
opt.method = "post";
main(opt);
}, get: function (opt) {
if (arguments.length >= 2 && (typeof arguments[0] == "string") && (typeof arguments[1] == "function")) {
opt = {
url: arguments[0],
success: arguments[1]
}; if (arguments[2] && (typeof arguments[2] == "function")) {
opt.error = arguments[2];
}
} opt.method = "get";
main(opt);
}
};

  先是头部这段代码,就是简单的做了一点封装,封装成了两个方法,一个是get,一个是post,但是其实两个最终调用的都是main方法。其中,opt则是要传入的参数。参数包括了url、请求对象,响应对象等。

  main方法如下

//转发请求主要逻辑
function main(opt) {
var options, creq; // res可以为response对象,也可以为一个可写流,success和error为请求成功或失败后的回调
opt.res = ((opt.res instanceof http.ServerResponse) || (opt.res instanceof stream.Writable)) ? opt.res : null;
opt.success = (typeof opt.success == "function") ? opt.success : noop;
opt.error = (typeof opt.error == "function") ? opt.error : noop; try {
opt.url = (typeof opt.url == "string") ? url.parse(opt.url) : null;
} catch (e) {
opt.url = null;
} if (!opt.url) {
opt.error(new Error("url is illegal"));
return;
} options = {
hostname: opt.url.hostname,
port: opt.url.port,
path: opt.url.pathname,
method: opt.method,
headers: {
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4,zh-TW;q=0.2',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.37 Safari/537.36'
}
}; // 如果req为可读流则使用pipe连接,传输数据,如果不是则直接写出字符串
if (opt.method == 'post') {
if (opt.req instanceof stream.Readable) {
if(opt.req instanceof http.IncomingMessage){
options.headers["Content-Type"] = opt.req.headers["content-type"];
options.headers["Content-Length"] = opt.req.headers["content-length"];
}
process.nextTick(function () {
opt.req.pipe(creq);
})
} else {
var str = ((typeof opt.req) == "string") ? opt.req : ""; process.nextTick(function () {
creq.end(str);
})
}
} else {
process.nextTick(function () {
creq.end();
})
} creq = http.request(options, function (res) {
reqCallback(opt.res, res, opt.success)
}).on('error', function (e) {
opt.error(e);
});
}

  首先将opt参数进行一些错误处理。其中res可以为响应对象,也可以为一个可写流。而success和error就是请求成功和失败后的回调,同时再将url转成对象方面后面使用。因为要在后台发起一个请求,所以请求的参数options是必须的啦。

  写好options后再判断要转发的请求是post还是get,如果是post而且传入的参数req是一个请求头或者可读流,则直接使用pipe连接res,进行数据传输。如果req是string,则直接写入。发起请求获得响应后则调用reqCallback方法,对数据进行处理。

  reqCallback方法如下:

//请求成功后的回调
function reqCallback(ores, res, callback) {
if (ores) {
ores.on('finish', function () {
callback();
}); if (ores instanceof http.ServerResponse) {
var options = {}; //复制响应头信息
if (res.headers) {
for (var k in res.headers) {
options[k] = res.headers[k];
}
} ores.writeHead(200, options);
} res.pipe(ores);
} else {
var size = 0;
var chunks = []; res.on('data', function (chunk) {
size += chunk.length;
chunks.push(chunk);
}).on('end', function () {
var buffer = Buffer.concat(chunks, size); //如果数据用gzip或者deflate压缩,则用zlib进行解压缩
if (res.headers && res.headers['content-encoding'] && res.headers['content-encoding'].match(/(\bdeflate\b)|(\bgzip\b)/)) {
zlib.unzip(buffer, function (err, buffer) {
if (!err) {
callback(buffer.toString())
} else {
console.log(err);
callback("");
}
});
} else {
callback(buffer.toString())
}
})
}
}

  对数据的处理比较简单,如果res是响应对象,则直接通过pipe连接,如果不是,则获取到数据,如果数据用gzip压缩了,则用zlib进行解压,然后放在回调中即可。

  transdata的调用比较简单,像get直接:

transdata.get(url , function(result){})

  而我项目中用到的数据转发的是用到post请求,也很简单,直接:

var transdata = require("transdata");
var http = require("http");
http.createServer(function(req , res){
transdata.post({
req:req,
url:'http://XXX/XX:9000/getdata',
res:res,
success:function(){
console.log("success");
},
error:function(e){
console.log("error");
}
});
})

  transdata写完,再回到上面那个demo的实现上来,既然有了transdata,获取数据就很容易了。代码如下:

var creeper = function(req , res , urlObj){
var header = fs.readFileSync(baseDir + "header.ejs").toString();
var contents = fs.readFileSync(baseDir + "contents.ejs").toString();
var foot = fs.readFileSync(baseDir + "foot.ejs").toString(); res.writeHead(200 , {'content-type':'text/html;charset=utf-8'});
res.write(ejs.render(header , {data:ids})); console.log("开始采集数据..."); var count = 0;
for(var i=0;i<ids.length;i++){
(function(index){
var id = ids[index];
var nowSource = source[id];
transdata.get(nowSource.url , function(result){
count++;
console.log(">【"+id+ "】get√"); var $ = cheerio.load(result);
var $colum = $(nowSource.colum); result = [];
$colum.each(function(){
result.push(nowSource.handle($(this)))
});
if(typeof +nowSource.max == "number"){result = result.slice(0 , nowSource.max)} if(result.length){
var data = {};
data[id] = result;
result.index = index; var html = ejs.render(contents , {data:data});
html = html.replace(/(\r|\n)\s*/g , '').replace(/'/g , "\\'");
res.write("<script>loadHtml("+index+" , 'dom_"+index+"' , '"+html+"')</script>");
} if(count == ids.length){
console.log("数据采集完成..");
res.end(foot);
}
})
}(i))
}
};

  获取到数据,数据为html信息,而处理html信息的工具就是cheerio,用法跟jquery的选择器一样,就用cheerio对数据进行操作并且获取自己需要的数据,这些就不进行赘述。相对比较简单。

  整个项目源代码的github地址附上:

  https://github.com/whxaxes/node-test/tree/master/server/creeper

  同时附上transdata.js的github地址:

  https://github.com/whxaxes/transdata

  有兴趣的可以一看。

nodejs学习之实现http数据转发的更多相关文章

  1. NodeJS学习笔记 (15)二进制数据-buffer(ok)

    模块概览 Buffer是node的核心模块,开发者可以利用它来处理二进制数据,比如文件流的读写.网络请求数据的处理等. Buffer的API非常多,本文仅挑选 比较常用/容易理解 的API进行讲解,包 ...

  2. Nodejs学习路线图

    前言 用Nodejs已经1年有余,陆陆续续写了48篇关于Nodejs的博客文章,用过的包有上百个.和所有人一样,我也从Web开发开始,然后到包管 理,再到应用系统的开发,最后开源自己的Nodejs项目 ...

  3. Nodejs学习笔记(四)——支持Mongodb

    前言:回顾前面零零碎碎写的三篇挂着Nodejs学习笔记的文章,着实有点名不副实,当然,这篇可能还是要继续走着离主线越走越远的路子,从简短的介绍什么是Nodejs,到如何寻找一个可以调试的Nodejs ...

  4. Nodejs学习笔记(六)--- Node.js + Express 构建网站预备知识

    目录 前言 新建express项目并自定义路由规则 如何提取页面中的公共部分? 如何提交表单并接收参数? GET 方式 POST 方式 如何字符串加密? 如何使用session? 如何使用cookie ...

  5. Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装项目其它需要包 清除冗余文件并重新规划项目目录 配置文件 规划示例路由,并新建相关文件 实现数据访问和业务逻辑相关方法 编写mys ...

  6. 使用Restify+superagent做数据转发

    最近为了解决跨域问题,做了一个Node数据转发服务器,使用到了Restify和superagent. Restify 是nodejs的模块.虽然restify的API或多或少的参考了express,但 ...

  7. [转]Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    本文转自:https://www.cnblogs.com/zhongweiv/p/nodejs_koa2_webapp.html 目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装 ...

  8. Nodejs学习笔记(十五)—Node.js + Koa2 构建网站简单示例

    前言 前面一有写到一篇Node.js+Express构建网站简单示例:http://www.cnblogs.com/zhongweiv/p/nodejs_express_webapp.html 这篇还 ...

  9. Nodejs学习笔记(六)—Node.js + Express 构建网站预备知识

    前言 前面经过五篇Node.js的学习,基本可以开始动手构建一个网站应用了,先用这一篇了解一些构建网站的知识! 主要是些基础的东西... 如何去创建路由规则.如何去提交表单并接收表单项的值.如何去给密 ...

随机推荐

  1. 常用的JAVA集合讲解

    java.util包中包含了一系列重要的集合类,而对于集合类,主要需要掌握的就是它的内部结构,以及遍历集合的迭代模式. 接口:Collection Collection是最基本的集合接口,一个Coll ...

  2. Python基础之装饰器

    1.什么是装饰器? Python的装饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pattern里的Decorator搞混了,其实这是完全不同的两个东西.虽然 ...

  3. shell脚本的执行

    shell脚本有两种执行方式,一种是直接执行,一种是使用$source 或.命令执行 直接执行 直接执行shell脚本,bash会在当前bash下新建一个子bash进程用来执行shell脚本,此时脚本 ...

  4. Linux 下从头再走 GTK+-3.0 (二)

    仅仅创建一个空白窗口是不够的,下面我们为创建的窗口添加一个按钮. 以 Hello,World!为例. 首先创建一个源文件:example2.c 内容如下. #include <gtk/gtk.h ...

  5. 新手必学的java报表开发工具FineReport实用技巧

    1.在制作模板时,如何将报表中的值传递到超链接网页呢? 在项目中以frame方法把F1.CPT放到项目的页面中,对F1.CPT做网络报表超链接F2.CPT,然后在F2.cpt页面中,做个超链接的网页, ...

  6. stanford coursera 机器学习编程作业 exercise4--使用BP算法训练神经网络以识别阿拉伯数字(0-9)

    在这篇文章中,会实现一个BP(backpropagation)算法,并将之应用到手写的阿拉伯数字(0-9)的自动识别上. 训练数据集(training set)如下:一共有5000个训练实例(trai ...

  7. switch结构的用法

    已知学生的名字和百分制分数.要求根据学生的百分制分数,分别采用"满分","优秀","良好","及格"和"不及格 ...

  8. 通过Hander进行界面刷新

    Timer timer; TimerTask task; Handler handler;//先声明这3个变量 //在onCreate方法内 handler = new Handler(){ @Ove ...

  9. AC日记——有趣的跳跃 openjudge 1.6 07

    07:有趣的跳跃 总时间限制:  1000ms 内存限制:  65536kB 描述 一个长度为n(n>0)的序列中存在“有趣的跳跃”当前仅当相邻元素的差的绝对值经过排序后正好是从1到(n-1). ...

  10. tyvj1098[luogu 2365]任务安排 batch

    题目描述 N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务.从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti.在每批任务开始 ...