
作业要求:
使用到的技术栈要求:
本次项目采用前后端分离架构,采用Node.JS作为后端,前端使用Express+Vue+Element UI框架,后台用户数据库使用Mysql,对爬虫数据的搜索使用elasticsearch,可视化功能使用echarts加kibana实现。
本次实验完成了所有的基本要求和扩展要求。
代码框架:
启动server文件夹内为后端代码,其余为vue前端代码。
期中app.js为运行express、nodejs的主文件,services.js为实现功能的主要文件,router.js为路由文件、db.js配置连接数据库和elasticsearch的文件。
前端实现只要在src文件夹下,主要由登录注册界面和查询界面的vue组件,控制前端路由的router文件和运行前端的main.js文件。
3、数据库schema数据库中有三个表,分别为fetches、ophistorys和users,分别为爬虫爬取到的新闻数据表、用户操作记录表和用户信息表。
其中fetch表和期中是用的一样。
ophistorys表含有以下字段
| username | operation | submission_date | |
|---|---|---|---|
| Text | Text | Text | Date |
users表含有以下字段
| id | username | password | avatar | info | verify_key | status | |
|---|---|---|---|---|---|---|---|
| Primary KEY | Text | Text | Text | Text | Text | Text | Minint |
期中id为主键,avator为标志位,verify_key为注册时的邮箱验证码(还未实现),status为用户状态,用于启动和禁用用户。
用户只有注册账号登录系统之后才能得到新闻爬虫网站中的数据。
1、用户注册用户注册时需要提交用户名称,用户密码和用户邮箱,前端接收到用户数据后传给后端由后端写入数据库,并将用户名、注册邮箱、注册操作和操作时间写入操作日志数据库中。
前端代码实现:
拓展功能:密码采用加密方式存储在数据库中
防止因为数据库数据泄漏导致用户密码泄漏,所以用户密码在数据库中采用加密存储,使用md5+一个自己定义的密钥进行存储。
在用户登陆时使用同样的加密方法同从数据库中获得的密码进行对比,相同则登录成功,不同则登录失败。用户注册时会将用户输入的密码加密后存储在数据库中。
加密模块为crypto.js文件
const crypto= require('crypto')
// 秘钥
const SECRET_KEY = 'WJio_8776#' //字符串自己设定的
// md5
function md5(content){
let md5 =crypto.createHash('md5') //
return md5.update(content).digest('hex') //把输出变成16进制
}
var genPassword=function(password){
const str =`password=${password}&key=${SECRET_KEY}`
return md5(str)
}
exports.genPassword = genPassword;
2、用户登录
用户登录操作需要用户的用户名和密码,前端接收用户名和密码发送到后台,后台验证用户名和密码是否相对应且用户的状态为启用状态,再将用户名作为参数跳转到数据搜索界面,使用登录拦截方法实现只有用户在登录过后才能访问搜索数据,如果直接访问搜索页面会被重定向到登录界面。
同时会将用户名、邮箱、登陆操作和操作时间写入操作日志数据库中。
Vue中实现登录拦截的方法:修改router.js文件,在需要登录拦截的路由中加入
meta: {
requireAuth: true
}
接着设置导航守卫函数,并绑定钩子函数
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth) { // 判断该路由是否需要登录权限
if (sessionStorage.getItem("token") == 'true') { // 判断本地是否存在token
next()
} else {
// 未登录,跳转到登陆页面
next({
path: '/login'
})
}
} else {
if(sessionStorage.getItem("token") == 'true'){
next('/search_home');
}else{
next();
}
}
});
记得在login页面中将sessionStorage的token设置为true
sessionStorage.setItem("token", 'true');
this.$router.push('/search_home');
三、爬虫数据搜索和展示功能
1、使用elasticsearch查询爬虫数据
通过爬虫得到的数据会存储在MySQL数据库中,而我们的查询功能需要使用elasticsearch提供的非结构化存储和快速的分词和按照查询结果打分的功能,故我们需要先将MySQL中的爬虫数据导入到elasticsearch中,具体导入过程需要使用logstash和mysql的驱动jar包,具体导入过程可参考:
自动将 MySQL 中的数据转入到 Elasticsearch 中 (logstash)
导入成功之后可以在elasticseach中看到我们从mysql中导入的数据
但这时我们的数据还不支持分词和按匹配程度打分,我们需要为我们在elasticsearch中索引添加映射让特定的字段支持分词,所以我们需要重新创建一个索引crawler_new,设置其mapping,让keywords、content和title字段支持分词,即为其设置 “analyzer”: “ik_smart” 属性(我们这里采用ik分词器,其使用可以参考ElasticSearch中文分词器-IK分词器的使用),新创建索引的mapping如下所示:
{
"crawler_new": {
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"@version": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
},
"analyzer": "ik_smart"
},
"crawltime": {
"type": "date"
},
"createtime": {
"type": "date"
},
"id_fetches": {
"type": "long"
},
"keywords": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
},
"analyzer": "ik_smart"
},
"publish_date": {
"type": "date"
},
"source_encoding": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"source_name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
},
"analyzer": "ik_smart"
},
"url": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
之后再将我们之前导入的crawler索引中数据导入crawler_inex索引即可。
导入之后的数据:
此时用我们的crawler_new索引中的数据,使用elasticsearch中的全文索引,使用elasticsearch自带的tf-idf得分对所搜索的结果进行打分可以实现查询结果按照主题词打分的排序。
前端的搜索功能支持按照title搜索或者按照content搜索内容,搜索得到的结果会以表格形式展示,搜索结果的表格支持分页功能,用户可以自定义每页的数据条数,可以对搜索得到的结果按照匹配度得分、数据发布时间和数据爬取时间进行排序,由于新闻内容字段可能过长会影响页面美观,我们之对其显示部分内容。
前端界面如下
分页功能和对查询结果的匹配度得分的排序或者时间的排序功能都由element-ui的接口提供。
前端代码实现:
总数量:{{total}}个查询 {{scope.row.content}}
后端代码实现:
exports.search=(req,res)=>{
let key_word=req.body.select_word;
let words=req.body.search_word;
if(key_word=="key_word"){
var search={
index: 'crawler_new',
body:{
query:{
match:{
keywords:words,
}
}
}
};
db.getES(search,function(query){
return res.json(query);
})
}
if(key_word=="content"){
var search={
index: 'crawler_new',
body:{
query:{
match:{
content:words,
}
}
}
};
db.getES(search,function(query){
return res.json(query);
})
}
}
这里使用elasticsearch的query搜索,得到对所搜索字段的tf-idf得分来作为该关键词的得分排序
四、图表显示项目要求中还要求对所获得的数据实现可视化展示功能,图表展示功能分为两个部分一个是使用echars的网页图表展示功能,一个是使用elastricsearch和kibnana的图表展示功能。
1、使用echarts进行图表展示安装echarts
npm install echarts --save
首先在我们的vue项目中引入echars,并将echars设置为全局变量,这样我们可以在其他文件中也可以使用echars
import echarts from 'echarts' Vue.prototype.$echarts = echarts
使用echarts组件实现热度分析功能,仅提供对关键词的所搜,当然也可以改成对内容和标题的搜索,都可以改的,我们对搜索得到的文章进行统计得到按出版时间计数的个数,并以图表形式表现出来(大于等于三张不同的图表)。
这里实现了四种图形的展示,分别是直方图、散点图、折线图和饼状图
前端代码: