之前刷微博的时候看过一个网站买下 Steam 所有游戏要花多少钱?,发现它的实现使用的是 Python后台NodeJS展示 。前几天 天二 发来了一个投票网站,在研究了一番之后找到了刷票的方法。再加上想用NodeJS干点事情,于是在这些乱七八糟的事情的驱动下,这个网站的想法就这样诞生的。NodeJS和Mongodb都是现学现卖的,至少能达到预期的效果了。

数据源网站分析

由于活动要到5月份才结束,为了不必要的麻烦,就不打算暴露网址了。也请自行脑补一些图片吧。

最初投票网站是分享到朋友圈的,通过分享的链接访问投票选手网页,点击“投票”就为该选手投上一票,多次投票只能算一票。

为了调试方便,将链接提取到电脑浏览器上,却提示 需要在微信中打开,最初想到是不是判断的UserAgent,于是找了一个Android微信浏览器的UserAgent试了一下,发现不行。这就纠结了,然后天二告诉说用iPhone的UserAgent是可以的,这…无语了…

Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365   MicroMessenger/5.4.1 NetType/WIFI

打开网页之后,需要研究投票逻辑。简单看了JS代码之后,发现投票按钮根本啥事情也没有做,那票是怎么投上去的?经过一系列的猜想和实验之后,发现了一个惊天的结论:网页通过Cookie判断是否多次投票,而投票只需访问一下网页就可以了…就可以了…就可以了…

天二说,好想去创业呀…

Python抓取数据

也好也好,网站简单程序也就简单了。一段投票+抓取数据的代码就这样诞生了:

1
2
3
4
5
6
7
8
9
import urllib2
url = 'http://balabala.com' # 数据源网址
useragent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365 MicroMessenger/5.4.1 NetType/WIFI'
request = urllib2.Request(url)
request.add_header('User-Agent', useragent) # 设置UserAgent
reader = urllib2.urlopen(request)
html = reader.read().decode('gbk')
print html # 打印网页源代码

网页源代码抓取到了,需要分析源代码的数据。这里主要抓取网页里的排行榜,排行榜在一个 table 中显示,而整个网页只有一个table。使用Python的HTMLParser库解析网页源代码。下面给出一个大致框架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from HTMLParser import HTMLParser
class MyHTMLParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.tag = None
def handle_starttag(self, tag, attrs):
if tag == 'td':
self.tag = 'td'
def handle_endtag(self, tag):
if self.tag == 'td':
... # 标签闭合时处理保存数据
self.tag = None
def handle_data(self, data):
if self.tag == 'td':
... # 临时记录数据
print data
... #省略抓取代码
parser = MyHTMLParser()
parser.feed(html)

然后就是定时任务了,不想使用无限循环,就参考使用crontab来执行定时任务。执行并在最后一行输入以下命令:

1
2
$ crontab -e
*/20 * * * * python main.py

这句话的意思是 每隔20分钟执行一次后面的命令,其它写法可以查阅相关资料。貌似后台数据端准备完毕了。

Mongodb数据库构建

选择Mongodb的原因有两个:一是不想用MySql了,二是没用过Mongodb。首先果然还是从安装搭配环境开始吧。

Windows下安装,首先到https://www.mongodb.org/downloads来下载安装包,安装之后找到安装目录,运行命令 mongod --dbpath "数据库路径" --logpath "日志路径\MongoDB.log" --install --serviceName "MongoDB",这样会以服务模式运行Mongodb数据库。

Linux下安装,也需要到https://www.mongodb.org/downloads来下载安装包,解压到\usr\local\mongodb目录下,创建配置文件mongodb.conf,配置开机启动。

mongodb.conf
1
2
3
4
5
dbpath=/usr/local/mongodb/db
logpath=/usr/local/mongodb/logs/mongodb.log
port=27017
fork=true
nohttpinterface=true
开机启动
1
2
vi /etc/rc.d/rc.local
/usr/local/mongodb/bin/mongod --config /usr/local/mongodb/bin/mongodb.conf

Python可以使用pymongo来连接Mongodb数据库,Windows下可以下载模块安装包,Linux下用pip安装:

1
2
3
4
5
6
import pymongo
import random
conn = pymongo.Connection("127.0.0.1",27017)
db = conn.test # 连接库
db.user.save({'id':1,'name':'kaka','sex':'male'}) # 插入一个数据

NodeJS需要mongodbmonk模块连接Mongodb数据库,使用npm安装:

1
2
3
4
5
6
7
8
var mongo = require('mongodb');
var monk = require('monk');
var db = monk('localhost:27017/test');
var collection = db.get('user');
collection.find({}, {}, function (e, timedocs) {
...
}

这里简单介绍了Mongodb的安装和连接方式。

NodeJS展示

有了数据和数据库,就需要一个展示页面了。框架选定了Express,第一次用说一下了解的地方。

安装 需要安装nodenpm,一般发行版的软件库中都包含吧,手动安装去找摆渡吧。

框架 如果有个IDE那就再好不过了,也可以手动下载Express。具体怎么安装还是看个人喜好吧。

路由 第一次接触这种需要手动书写路由规则的方式。Express的路由规则分为两部分,app.jsroutes目录下的文件。首先在app.js中指定哪个目录由哪个路由解析,然后在routes下配置相应的路由文件。

数据库 数据库最好是维持一个连接,而不是每有一个请求到来就建立一个连接。实现的方式是在app.js中建立连接,然后在routes中引用这个数据库连接。这里贴出一个app.js的代码,可以参考这段代码的注释。在路由中通过req.db就可以取到数据库引用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
// 数据库连接放到这里
var mongo = require('mongodb');
var monk = require('monk');
var db = monk('localhost:27017/nodetest1');
var routes = require('./routes/index');
var users = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// 将db分发到router里,注意放在'\'路由的前面
app.use(function(req,res,next){
req.db = db;
next();
});
app.use('/', routes); // ---放在**这**前面---
app.use('/users', users);
/// catch 404 and forwarding to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
/// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;

启动 开发环境和部署环境的运行是不同的,部署环境下需要能在后台运行。可以在网站目录下运行 PORT=3000 nohup npm start & 命令,PORT通过环境变量来修改启动端口,nohop配合&实现后台执行。

其它 诸如如何书写页面,如何传参渲染,这些问题可以参考Express框架里的默认页面。

服务器和域名

服务器使用阿里云的服务器,将以上内容部署上去之后打算绑上一个域名,参考 土豪 那篇文章,打算就使用Nginx做转发。

安装Nginx,打开配置文件vim /etc/nginx/nginx.conf,添加一段内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
server_name steamtuhao.com www.steamtuhao; # ←写你的域名
location / {
proxy_pass http://127.0.0.1:3000; # ←写你的端口
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}

注意域名和端口。然后重启Nginx service nginx restart

很顺利,不过遇到了一个问题,域名需要备案。算了,还是乖乖用IP访问吧。

总结

这次实现过程用了一天多的时间,其实主要的时间都用在研究图表插件的展示问题,这里没有提到这部分内容。

整个实现的功能就是,Python每隔20分钟抓取一次数据存储到Mongodb数据库中,Express读取Mongodb展示出来。

至此这部分内容可以告一段落了。

参考文献