JAVAScript爬取数据实现用户登录与可视化

JAVAScript爬取数据实现用户登录与可视化

任务

在这里插入图片描述
由于最近时间太紧了,权衡下,只选择完成最基本的要求了。
在期中的大作业中,我已经完成了腾讯,网易和搜狐这三个网页的信息爬取和结构化存储。在这里就直接使用数据库中的这些数据,不作新的数据采集工作。
之前作业的链接:
https://blog.csdn.net/nbyvy/article/details/116300934?spm=1001.2014.3001.5501
本次代码github链接(注并非原创,大多数是课堂上老师给的参考模版):
https://github.com/zgl-ai/zgl-ai-javascript_clawer_visuable

功能一、登入登出与注册

首先是用户的注册登入登出操作,那么就需要在后台的后端的数据库中维护一张用户信息表user。

建表操作

--创建用户信息数据表
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;

用了username,password,registertime这三个属性。

后端实现

位于public/routes/users.js中。
注册实现函数:

/* add users */
router.post('/register', function (req, res) {
var add_user = req.body;
// 先检查用户是否存在
userDAO.getByUsername(add_user.username, function (user) {
if (user.length != 0) {
// res.render('index', {msg:'用户不存在!'});
res.json({msg: '用户已存在!'});
}else {
userDAO.add(add_user, function (success) {
res.json({msg: '成功注册!请登录'});
})
}
});

});

通过路由/register接受前端请求数据,前端会传来的如下json格式的数据:
在这里插入图片描述
接收到数据后,通过userDAO.getByUsername,连接数据库的user表,并且查询username与传来数据相匹配的数据。
这是我们实现在文件夹dao中写好的函数。userDAO.js负责连接数据库中的user表,
并且定义了两个函数:

add: function (user, callback) {
pool.query(userSqlMap.add, [user.username, user.password], function (error, result) {
if (error) throw error;
callback(result.affectedRows > 0);
});
},
getByUsername: function (username, callback) {
pool.query(userSqlMap.getByUsername, [username], function (error, result) {
if (error) throw error;
callback(result);
});
},

一个是为了方便向数据库中添加元组的add,和一个通过username查询元组的getByUsername。这样以后就很方便的调用该模块中的两个函数来执行sql语句。
回到users.js
在查询数据库之后会做一次判断,如果数据库返回结果为空,那就返回注册成功,执行add操作,向数据库中写入注册的用户信息;
如果数据库返回不为空,那么就输出一条(“用户已存在”),不作其他处理。

登录实现函数

router.post('/login', function(req, res) {
var username = req.body.username;
var password = req.body.password;
// var sess = req.session;

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:'用户名或密码错误!请检查后输入'});
}
}
});
});

通过/login路由接受前端传来的如下json格式数据:
在这里插入图片描述
接受到数据之后调用getByUsername函数,查询数据库的相应记录。如果没有查到,那么就表明用户数据库中没有该用户信息,不能登录成功。
查到记录之后还需要再便定用户输入的密码和查询到的密码是不是一致,如果一致,那么登录成功并且将用户的username存入cookie,计入session数组中;
如果不一致那么就是没有登录成功。

登出实现函数

// 退出登录
router.get('/logout', function(req, res, next){
// 备注:这里用的 session-file-store 在destroy 方法里,并没有销毁cookie
// 所以客户端的 cookie 还是存在,导致的问题 --> 退出登陆后,服务端检测到cookie
// 然后去查找对应的 session 文件,报错
// session-file-store 本身的bug

req.session.destroy(function(err) {
if(err){
res.json('退出登录失败');
return;
}
// req.session.loginUser = null;
res.clearCookie('username');
res.json({result:'/index.html'});
});
});

路由/logout接收到登出请求后,只需要清空session和cookie就行了。

前端的实现

用的是老师给的参考模版。
除了一些html元素和css样式之外,还用到了一些脚本,比如输入不能为空,组册页面两次密码的输入必须一致,满足条件后向后端特定的路由传递数据等。
这里代码方面不作详细解读。
在这里插入图片描述

功能二、用户操作日志

就是将用户的请求的信息写入数据库中。
因此还需要建立一张表:

--记录用户的登陆,查询(具体查询语句)操作
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;

记录用户名,请求时间,请求的方式,请求的链接,请求的状态,远程用户等信息。
同样在dao中的logDAO中,连接了数据库中的user_action表,并且定义了向数据库添加元组的userlog函数,以便后期调用快速实现sql语句的输入:

userlog :function (useraction, callback) {
pool.query('insert into user_action(username,request_time,request_method,request_url,status,remote_addr) values(?, ?,?,?,?,?)',
useraction, function (error, result) {
if (error) throw error;
callback(result.affectedRows > 0);
});
},

实现该功能的后端代码位于app.js中:

let method = '';
app.use(logger(function (tokens, req, res) {
console.log('打印的日志信息:');
var request_time = new Date();
var request_method = tokens.method(req, res);
var request_url = tokens.url(req, res);
var status = tokens.status(req, res);
var remote_addr = tokens['remote-addr'](req, res);
if(req.session){
var username = req.session['username']||'notlogin';
}else {
var username = 'notlogin';
}

// 直接将用户操作记入mysql中
if(username!='notlogin'){
logDAO.userlog([username,request_time,request_method,request_url,status,remote_addr], function (success) {
console.log('成功保存!');
})
}

每次的请求的会传入:
通过new Date()存储每次的接受到请求的时间;
通过tokens,存储请求的状态、方法、链接和远程用户;
通过session,存储username;
最终通过调用之前就定义好的uselog的方法,直接使用sql的insert语句存入数据库。

此外,日志存数据库后,后台还将日志信息打印出来(代码比较简单,不作展示了):
在这里插入图片描述

功能三、查询

主要是实现关键词以及逻辑连接词的组合查询。
先上前端页面:
在这里插入图片描述

向输入框中输入要查询的关键词,并且在AND和OR中选择一个,进行查询。
这个通过输入的内容来选择相应的sql语句来来实现。在dao中的newsDAO.js中定义search函数来构建相应的sql语句:

search :function(searchparam, callback) {
// 组合查询条件
var sql = 'select * from fetches ';

if(searchparam["t2"]!="undefined"){
sql +=(`where title like '%${searchparam["t1"]}%' ${searchparam['ts']} title like '%${searchparam["t2"]}%' `);
}else if(searchparam["t1"]!="undefined"){
sql +=(`where title like '%${searchparam["t1"]}%' `);
};
if(searchparam["t1"]=="undefined"&&searchparam["t2"]=="undefined"&&searchparam["c1"]!="undefined"){
sql+='where ';
}else if(searchparam["t1"]!="undefined"&&searchparam["c1"]!="undefined"){
sql+='and ';
}
if(searchparam["c2"]!="undefined"){
sql +=(`content like '%${searchparam["c1"]}%' ${searchparam['cs']} content like '%${searchparam["c2"]}%' `);
}else if(searchparam["c1"]!="undefined"){
sql +=(`content like '%${searchparam["c1"]}%' `);
}
if(searchparam['stime']!="undefined"){
if(searchparam['stime']=="1"){
sql+='ORDER BY publish_date ASC ';
}else {
sql+='ORDER BY publish_date DESC ';
}
}
sql+=';';

大致的原理就是:
不断判定搜索框中的字段是不是为空;
如果不是的就从数据库中对应的属性中查询。

快速构建sql之后,就是调用了。具体的实现在routes/new.js中:

outer.get('/search', function(request, response) {
console.log(request.session['username']);
//sql字符串和参数
if (request.session['username']===undefined) {
// response.redirect('/index.html')
response.json({message:'url',result:'/index.html'});
}else {
var param = request.query;
newsDAO.search(param,function (err, result, fields) {
response.json({message:'data',result:result});
})
}
});

就是从/search路由中接受前端传来的数据,并且调用search函数查询并且返回。

功能四、查询结果分页展示

这个是在前端文件search.html文件实现的。当然分页展示,在网上都是有很多模版的。
后端查询结果得到后返回前端。

<table class="table table-striped">
<thead>
<tr>
<td>序号</td>
<td>标题</td>
<td>作者</td>
<!-- <td>内容</td>-->
<td>关键词</td>
<td>链接</td>
<td>发布时间</td>
</tr>

</thead>
<tbody>
<tr ng-repeat="(key, item) in items">
<td>{{index+key}}</td>
<td>{{item.title}}</td>
<td>{{item.author}}</td>
<!-- <td>{{item.content}}</td>-->
<td>{{item.keywords}}</td>
<td>{{item.url}}</td>
<td>{{item.publish_date}}</td>
</tr>
</tbody>
</table>

将返回的元素全部放置于table中。

<a ng-click="selectPage(page)" >{{ page }}</a>

通过这一条语句分页展示出来。其中的脚本函数在public/javascript/new.js中。
在这里插入图片描述

按时间排序就在数据库查询是判定一下是不是需用按照时间生序降序就好了:

if(searchparam['stime']!="undefined"){
if(searchparam['stime']=="1"){
sql+='ORDER BY publish_date ASC ';
}else {
sql+='ORDER BY publish_date DESC ';
}

功能五、可视化

柱状图(新闻数量-日期)
在这里插入图片描述

前端news.html点击后,启动脚本。Histogram()将请求发送给后端:

<li><a ng-click="histogram()">柱状图</a></li>
<li><a ng-click="pie()">饼状图</a></li>
<li><a ng-click="line()">折线图</a></li>
<li><a ng-click="wordcloud()">词云</a></li>

后台的代码位于routes/new.js中:

router.get('/histogram', function(request, response) {
//sql字符串和参数
console.log(request.session['username']);

//sql字符串和参数
if (request.session['username']===undefined) {
// response.redirect('/index.html')
response.json({message:'url',result:'/index.html'});
}else {
var fetchSql = "select publish_date as x,count(publish_date) as y from fetches group by publish_date order by publish_date;";
newsDAO.query_noparam(fetchSql, function (err, result, fields) {
response.writeHead(200, {
"Content-Type": "application/json",
"Cache-Control": "no-cache, no-store, must-revalidate",
"Pragma": "no-cache",
"Expires": 0
});
response.write(JSON.stringify({message:'data',result:result}));
response.end();
});
}
});

在路由/histogram中接受前端的请求。

接受到请求之后,从数据查询publish_date与其计数,并且返回给前端:

select publish_date as x,count(publish_date) as y from fetches group by publish_date order by publish_date;

前端接受到数据之后,在histogram()中绘制柱状图。
调用了echarts可以方便绘制多种图像。

$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 {
// var newdata = washdata(data);
let xdata = [], ydata = [], newdata;
var pattern = /\d{4}-(\d{2}-\d{2})/;
res.data.result.forEach(function (element) {
// "x":"2020-04-28T16:00:00.000Z" ,对x进行处理,只取 月日
xdata.push(pattern.exec(element["x"])[1]);
ydata.push(element["y"]);
});
newdata = {"xdata": xdata, "ydata": ydata};
var myChart = echarts.init(document.getElementById('main1'));
// 指定图表的配置项和数据
var option = {
title: {
text: '新闻发布数 随时间变化'
},
tooltip: {},
legend: {
data: ['新闻发布数']
},
xAxis: {
data: newdata["xdata"]
},
yAxis: {},
series: [{
name: '新闻数目',
type: 'bar',
data: newdata["ydata"]
}]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
}
},
function (err) {
$scope.msg = err.data;
});
};

同理,折线图,饼状图,词云的机制大致类似,其中用到了jieba分词,词频统计等函数实现,都是套用了参考模版,不作详细的展示了。
直接上图展示:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

  • 至此所有的基本功能都实现了。由于本人的拖延症作祟,导致到大作业和期末考试全挤在了一起,很遗憾没能有时间精力继续完成扩展功能。首先反省,下次一定调整好进度!!
  • 前后端分离的软件架构。在web编程的学习过程,我最有体会的就是了解到前后端分离的软件架构,包括了前端怎么和后端nodejs传输数据,前端的使用css样式和js脚本,前端接受后端的数据并且展示,后端处理请求接受并且返回数据等等。只能说刚刚入门。
  • 自我感觉,js语法是我目前学习的语言中最难的了。虽然基本的语法,比如基本数据类型循环、选择结构,函数调用等是编程中比较通用的也是比较好上手,但是js也有自己具有特色的难点,比如函数的异步执行和嵌套使用,数据的路由,前端的js脚本等等。
  • 这次实现之后也算是用来之后写一些简易的前后端的网页app。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nbyvy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值