期末大作业展示:
一、项目要求
1、用户可注册登录网站,非注册用户不可登录查看数据
2、用户注册、登录、查询等操作记入数据库中的日志
3、爬虫数据查询结果列表支持分页和排序
4、用Echarts或者D3实现3个以上的数据分析图表展示在网站中
5、实现一个管理端界面,可以查看(查看用户的操作记录)和管理(停用启用)注册用户。
二、简单回顾
我们希望实现这样一个有着基本注册、查询功能的网站。在之前的工作中,我已经搭建好了一个简单的网站,来实现简单的关键词查找功能,请看:
三、准备工作
1、express框架安装
express是一个简洁而灵活的node.js web应用框架,提供一系列强大特性帮助创建各种web应用。
使用express可以快速地搭建一个功能完整的网站。
安装过程
npm install -g express-generator
这样准备工作已经完成。
2、项目框架
2.1 数据库准备
CREATE TABLE `crawl`.`user` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(45) NOT NULL,
`password` VARCHAR(45) NOT NULL,
`registertime` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username_UNIQUE` (`username`))ENGINE=InnoDB DEFAULT CHARSET=utf8;
这是在crawl数据库中建立了一个名叫user的表格。为了记录该网站的用户信息。
CREATE TABLE `crawl`.`user_action` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(45) NOT NULL,
`request_time` VARCHAR(45) NOT NULL,
`request_method` VARCHAR(20) NOT NULL,
`request_url` VARCHAR(300) NOT NULL,
`status` int(4),
`remote_addr` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`))ENGINE=InnoDB DEFAULT CHARSET=utf8;
以上是建立了一个名叫user_action的表格。一个用来查看注册的用户;另一个用来记录用户的行为。
注意:以上两个表格都是在数据库中创建的,即将该段代码写入mysql中。
2.2 数据库配置
module.exports = {
mysql: {
host: 'localhost',
user: 'root',
password: 'root',
database:'crawl',
// 最大连接数,默认为10
connectionLimit: 10
}
};
这样做的目的是建立一个连接mysql的配置文件。
2.3 用户界面
我们希望看到,登录时:如果用户名或密码错误,将发起提醒;如果用户没有注册,则提醒用户不存在。注册时:如果两次密码不一致,进行提醒;如果用户已经存在,给出提醒;如果新用户注册成功,自动跳转登录页面。
首先cmd运行我的代码,这时出现了登录or注册页面:
若一个新用户点击“login”键,会自动跳转“register”页面。
之后,会有关于新闻搜索的界面显示。同时,点击“图标”键出现柱状图、折线图等的直观分析。
四、功能实现
3.1 登录功能
在网页端,用check函数检查用户密码是否输入正确,如果输入正确,会跳转到news.html界面,即成功登陆。如果密码错误,就会提出警告。
$scope.check_pwd = function () {
var data = JSON.stringify({
username: $scope.username,
password: $scope.password
});
$http.post("/users/login", data)
.then(
function (res) {
if(res.data.msg=='ok') {
window.location.href='/news.html';
}else{
$scope.msg=res.data.msg;
}
},
function (err) {
$scope.msg = err.data;
});
3.2 注册功能
注册功能和登录是类似的,其中,第一遍add_password输入用户密码,然后confirm_password用来确认密码。
<form id="register-form" method="post" role="form" style="display: none;">
<div class="form-group">
<input ng-model="add_username" tabindex="1" class="form-control" placeholder="Username" value=""/>
</div>
<div class="form-group">
<input type="password" ng-model="add_password" tabindex="2" class="form-control" placeholder="Password">
</div>
<div class="form-group">
<input type="password" ng-model="confirm_password" tabindex="2" class="form-control" placeholder="Confirm Password">
</div>
<div class="form-group">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<button tabindex="4" class="form-control btn btn-register" ng-click="doAdd()">Register Now</button>
</div>
</div>
</div>
</form>
同理,如果用户错误操作,会显示警告:
<div class="alert alert-warning" ng-if="msg && msg!='ok'">
<a href="#" class="close" data-dismiss="alert">×</a>
<strong>警告!</strong>{{msg}}
</div>
报错情况:密码两次不一致
3.3 路由代码
当获取到密码时,将自动检测匹配度;
如果输入密码正确,发送一个json响应,同时“ok”,同时将此次操作保存到服务器内部;反之,如果密码错误或其他,msg不会有正确提示。
var userDAO = require('../dao/userDAO');
userDAO.getByUsername(username, function (user) {
if(user.length==0){
res.json({msg:'用户不存在!请检查后输入'});
}
else {
if(password===user[0].password){
req.session['username'] = username;
res.cookie('username', username);
res.json({msg: 'ok'});
// res.json({msg:'ok'});
}
else{
res.json({msg:'用户名或密码错误!请检查后输入'});
}
3.4 查看日志(管理端界面)
最开始我认为日志需要我记录下每一次操作痕迹,手动写入数据库中。经过与同学们的交流,我了解到可以运用node.js中的express框架使用Morgan中间件记录日志。
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var logger = require('morgan');//Morgan中间件记录日志
另外,session技术可以帮助我。在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
app.use(session({
secret: 'sessiontest',
resave: true,
saveUninitialized: false,
cookie : {
maxAge : 1000 * 60 * 60,
},
}));
在这一番操作之后,我得到了想要的结果。
进入我的数据库,输入:
select * from user_action;
显示:
3.5 查询功能
为了实现查询词支持布尔表达式,我去了解了一下有关概念。
即输入“中国”and“世界”,所出现的内容标题必须包含这两个关键词;而输入“中国”or“世界”,所出现的内容标题要么包含“中国”,要么包含“世界”。
查询数据:
$scope.search = function () {
var title1 = $scope.title1;
var title2 = $scope.title2;
var selectTitle = $scope.selectTitle;
var content1 = $scope.content1;
var content2 = $scope.content2;
var selectContent = $scope.selectContent;
var sorttime = $scope.sorttime;
有时读者不输入特殊词,则默认显示所有信息(利用正则表达式):
var myurl = `/news/search?t1=${title1}&ts=${selectTitle}&t2=${title2}&c1=${content1}&cs=${selectContent}&c2=${content2}&stime=${sorttime}`;
结果是:
3.6 分页功能
这里再简单地说明一下,查询到的新闻如何进行分页,我选择直接在HTML文件中实现分页功能(利用angular实现):
<div class="pull-right">
<nav>
<ul class="pagination">
<li>
<a ng-click="Previous()" role="button"><span role="button">上一页
</span></a>
</li>
<li ng-repeat="page in pageList" ng-class="{active:isActivePage(page)}" role="button">
<a ng-click="selectPage(page)" >{{ page }}</a>
</li>
<li>
<a ng-click="Next()" role="button"><span role="button">下一页</span>
</a>
</li>
</ul>
</nav>
</div>
为了页面的简洁,我选择一页展示4条新闻,最多不展示超过5页的内容:
$scope.initPageSort=function(item){
$scope.pageSize=4; //每页显示的数据量是可以随意更改的
$scope.selPage = 1;
$scope.data = item;
$scope.pages = Math.ceil($scope.data.length / $scope.pageSize);
//分页数
$scope.pageList = [];//后面6页之后不会全部列出
$scope.index = 1;
var len = $scope.pages> 5 ? 5:$scope.pages;
$scope.pageList = Array.from({length: len}, (x,i) => i+1);
//表格数据源
$scope.items = $scope.data.slice(0, $scope.pageSize);
展示一下:
3.7 排序功能
杂乱的信息无法给人一个好印象,我将用时间排序,来使信息有序起来:
<div class="pull-left" style="margin-top: 12px;">
<button type="submit" class="btn btn-primary" ng-click="searchsortASC()" >升序排列</button>
<button type="submit" class="btn btn-primary" ng-click="searchsortDESC()">降序排列</button>
</div>
展示:
3.8 Echart图表
经过老师的介绍,我了解到Echart.js的作用:ECharts,一个纯 Javascript 的图表库,可以流畅的运行在PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖轻量级的 Canvas 类库 ZRender,提供直观,生动,可交互,可高度个性化定制的数据可视化图表。
ECharts 提供了常规的折线图,柱状图,散点图,饼图,K线图,用于统计的盒形图,用于地理数据可视化的地图,热力图,线图,用于关系数据可视化的关系图,treemap,多维数据可视化的平行坐标,还有用于 BI 的漏斗图,仪表盘,并且支持图与图之间的混搭。
那么如何应用到我的网页中呢?
首先,我想做一个词云:
$scope.wordcloud = function () {
$scope.isShow = false;
$http.get("/news/wordcloud").then(
function (res) {
if(res.data.message=='url'){
window.location.href=res.data.result;
}else {
var mainContainer = document.getElementById('main1');
var chart = echarts.init(mainContainer);
var data = [];
for (var name in res.data.result) {
data.push({
name: name,
value: Math.sqrt(res.data.result[name])
})
}
var maskImage = new Image();
maskImage.src = './images/logo.png';
var option = {
title: {
text: '所有新闻内容的鲸鱼词云'
},
展现如下:
(关键词为“中国”)
其次,我将要做一个展现不同时期新闻数量的柱状图,
$scope.histogram = function () {
$scope.isShow = false;
$http.get("/news/histogram")
.then(
function (res) {
if(res.data.message=='url'){
window.location.href=res.data.result;
}else {
let xdata = [], ydata = [], newdata;
var pattern = /\d{4}-(\d{2}-\d{2})/;
res.data.result.forEach(function (element) {
xdata.push(pattern.exec(element["x"])[1]);
ydata.push(element["y"]);
});
newdata = {"xdata": xdata, "ydata": ydata};
var option = {
title: {
text: '有关新闻发布数量随时间的变化柱状图'
},
tooltip: {},
legend: {
data: ['新闻发布数']
},
xAxis: {
data: newdata["xdata"]
},
yAxis: {},
series: [{
name: '新闻数目',
type: 'bar',
data: newdata["ydata"]
}]
};
},
};
(代码中已经指定了横轴和纵轴)
最后,我将用一个饼状图来展示数据库中新闻编辑的信息:
$scope.pie = function () {
$scope.isShow = false;
$http.get("/news/pie").then(
function (res) {
if(res.data.message=='url'){
window.location.href=res.data.result;
}else {
let newdata = [];
var pattern = /责任编辑:(.+)/;
res.data.result.forEach(function (element) { newdata.push({name: pattern.exec(element["x"]), value: element["y"]});
});
为了这个饼状图我想了两天整,试了很多种方法,还是报错;最后,一次和别人的交流启发了我:是不是传参数的时候出现问题了?之后我发现,一个变量没有定义,删掉重新修改后,展示如下:
胜利!
还没完哦,最后还有一个折线图。为了解近段时间以来我国有关疫情的报道数量,我将用折线图展示。一开始,这个折线图怎么也出现不了,不是没有横纵坐标轴就是没有数据,但是网页也没有报错,还是代码出了问题。一开始,我原有的一行代码为ydata.push(element[“count(*)”]);此时仅有坐标轴而没有数据;经过修改为ydata.push(element[“count”]);成功出现了数据。
$scope.line = function () {
$scope.isShow = false;
$http.get("/news/line").then(
function (res) {
console.log(res);
if(res.data.message=='url')
{
window.location.href=res.data.result;
}
else{
let xdata = [], ydata = [], newdata;
var pattern = /\d{4}-(\d{2}-\d{2})/;
res.data.result.forEach(function(element){
console.log(element);
xdata.push(pattern.exec(element["publish_date"])[1]);
//ydata.push(element["count(*)"]);
ydata.push(element["count"]);
});
newdata = {"xdata":xdata, "ydata":ydata};
var myChart = echarts.init(document.getElementById("main1"));
折线图如下:
至此,四个图全部做好了。
五、项目回顾
通过本次项目的实践,我感受到了研究前后端实现的乐趣。虽然期间也遇到了很多困难,比如各种花样百出的报错信息、界面无法显示等等。但是通过自己的思索,还是明白了解决这些问题的方法。由于自己能力所限,页面仍然称不上美观(页面红配绿是我本人了),但是对web编程的理解更加深刻了。
最后,感谢老师和助教的指导,你们辛苦啦!