node.js 爬虫服务端

215 阅读2分钟

为啥用这个爬数据?py 不香吗?

回答:简单,js 方便在页面上验证,不用转换。

项目结构如下: 工程地址

├─ bin          # 二进制执行脚本: npm start 会调这块
├─ node_modules # 利用npm管理的所有包及其依赖
├─ config       # 配置文件:redis、mongodb、mysql 配置信息
├─ package.json # npm的配置文件:脚本、依赖等
├─ lib          # 库:封装的一些工具
├─ proxy        # 代理:管理连接 redis、mongodb、mysql
├─ public       # 公共资源:一些样式
├─ views        # 网页:127.0.0.1:8080 的样式等
├─ test         # 测试:测试连接是否成功,目前只有测试 redis
├─ routes       # 路由:目前有 3 个接口,/ 首页、/worker/:event post接口、/health 健康
└─ app.js       # 工程入口:网络框架 express

服务端这块主要功能是:存储。

运行命令有:

  • npm test:用于测试连接,目前只有测试 Redis
  • npm start:用 pm2 启动
  • npm stop : 用 pm2 停止

其实这块对应脚本就在 package.json里:

先跑起来

工程 clone下来:

工程基于 npmmac下可使用 brew install npm进行安装。

首先依赖下载,直接运行:npm install

预先准备 redis mysql

# redis
$ docker run -itd --name redis-test -p 6379:6379 redis

# mysql
$ docker run -itd --name mysql-test -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql

配置对应文件 config/local.json

{
  "redis": {
    "host": "127.0.0.1",
    "port": "6379"
  },
  "mysql": {
    "connectionLimit": "15",
    "host": "127.0.0.1",
    "port": "3306",
    "user": "user",
    "password": "root",
    "database": "123456"
  }
}
  1. 测试 redis连接是否正常: npm test

忽略 Configuration property "redis.cluster" is not defined即可。

  1. 运行: npm start

  1. 查看日志: pm2 logs
  1. 打开 http://127.0.0.1:8080/****

综上工程算跑起来了。

源码解析

服务端这块东西较少,主要是存储。会对接 redismongodbmysql

  1. 首先了解入口 app.js : 网络框架使用的是 express
  • 规定一些请求的设置:HTTPbody等。
  • 规定路由规则:routes/index
router.get('/', function (req, res) {
    res.render('index', {title: 'schedule-server'});
});
router.post('/worker/:event', function (req, res) {
    var event = req.params.event;
    var collection = req.body;
    worker.emit(event, req, res, collection);
});
router.get('/health', function (req, res) {
    res.send({});
});
  1. 熟悉 worker.js

在上面路由中可看到:worker.emit(event, req, res, collection);触发一个事件

主要是根据 event调用不同的方法。

worker.js源码如下:

function Worker() {
    // call方法使得 Worker 对象继承了 EventEmitter 对象上的方法
    events.EventEmitter.call(this); 
    var prototypes = this.funs; // 所有方法
    for (var i = 0; i < prototypes.length; i++) {
        // on 绑定一个事件 
        this.on(prototypes[i], this[prototypes[i]]);
    }
}
// 使用 inherits 方法, 功能与 call 有点重复
// inherited: 是基与原型的继承
// call: 是基于对象继承,可以获取对象方法
util.inherits(Worker, events.EventEmitter);

对应比如: EventEmitter

//event.js 文件
var events = require('events'); 
var emitter = new events.EventEmitter(); 
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener1', arg1, arg2); 
}); 
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener2', arg1, arg2); 
}); 
emitter.emit('someEvent', 'arg1 参数', 'arg2 参数'); 

// 执行
$ node event.js 
listener1 arg1 参数 arg2 参数
listener2 arg1 参数 arg2 参数

剩下就是定义了一堆方法: 举个栗子

Worker.prototype.find = function (req, res, collection) {
    var name = collection.name;
    var data = collection.data;
    var sort = collection.sort;
    if (sort == null) {
        sort = {};
    }
    mongodb.find(name, data, sort, function (err, result) {
        if (err) {
            console.error('[%s]-[find] - 查询数据异常., error_message : [%s]', __dirname, JSON.stringify(err));
            res.send({'result': '[find] - 查询数据异常.', 'is_success': false});
        } else {
            res.send({'result': result, 'is_success': true});
        }
    });
};

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情