Web编程期末实践——Angular+nodejs-express搭建简易新闻查询网站


·
本项目在期中项目(指路链接: Web编程期中实践——Nodejs新闻爬虫及查询网站)的基础上修改完成。
·

项目要求

项目要求

数据库设计

该项目我们在crawl数据库中设计了3个表:fetches、user和user_action。
在这里插入图片描述
(1)fetches表同期中项目的设计一样,只不过我们在此处修改优化了当时的爬虫代码,利用正则表达式规范爬取的数据,比如对author一栏,确保获得并存储进数据库的作者名足够“干净”,有利于后续在网页中的数据展示。

(2)user表包含基本用户信息(姓名、密码、注册时间),此外还设置powerstate两项来记录 用户身份(管理员or普通用户)和 状态(正常or冻结)。
同时,我们人为向数据库中插入一条记录,创建管理员admin。

insert into crawl.user(username, password, power, state) values('admin', 12345, 'super', 'normal');

(3)user_action表存放所有用户在该网站上的所有操作记录(如请求时间、用户所在IP地址、请求内容、响应状态等)。
·

基础配置

如图,我们在express搭建框架生成的search_site文件中进行一些基础的配置(express框架搭建见我们期中项目博客)。
在mysqlConf.js中配置mysql连接参数;
在这里插入图片描述
在app.js中设置路由路径、导入angular框架、设置session参数等,以及设置将用户的操作记录存入数据库。

var usersRouter = require('./routes/users');
var newsRouter = require('./routes/news');
......

·

用户注册、登录及登出

我们在index.html上实现了一个简洁明了的登录和注册窗口,能根据用户的操作进行相应提示。

  1. 注册:
    a.用户已存在;
    b.两次输入密码不一致;
    c.注册成功后自动跳转登录界面。
  2. 登录:
    a. 用户名或密码错误;
    b. 用户被冻结,无法登陆;
    c. 用户不存在,请先注册。
    在这里插入图片描述
    同时,我们也不忘设计对未登录用户,当其处于非登录页面时,不允许其查看网页,并强制其跳转登录(此处的设计仍不够完善,通过用户触发操作函数时判断是否有登录用户来达到该目的,存在耦合、冗余的问题,之后有待改进)。

登出选项我们则设置在网站各页面的导航栏中,一旦退出,则清除session。
在这里插入图片描述

·

新闻查询

由于我们将新闻查询和图表展示的内容置于同一个界面news.html,结构稍显耦合、复杂,故通过ng-showng-hide两个参数来调控页面的展示布局。
这里isShow作用于所有图表,isShow2控制折线图的查询框。通过二者合取的结果来控制新闻查询窗口的显示与否。我们默认初始时显示新闻查询窗口。

<div ng-show="isShow && isShow2" style="width: 1150px;position:relative; top:70px;left: 80px">
    <!--    查询页面-->
    <div ng-include="'search.html'"></div>
</div>

对于新闻查询,我们实现了多条件组合查询,可同时对标题、内容、作者和来源搜索,并且对标题和内容关键字还配备布尔查询AND和OR选项。
结果页面简洁明了,直接展示总结果记录数;对标题赋予超链接,用户可直接点击跳转到新闻页面;同时还实现新闻按时间升序、降序排列;以及分页展示结果。
在这里插入图片描述
查询页路由 routes/news.js:

router.get('/search', function(request, response) {
    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});
        })
    }
});

查询结果展示 public/search.html:

<!--isisshowresult:控制显示查询结果-->
<div ng-show="isisshowresult" style="padding:0 1em;">
    <p><b>共 {{datasize}} 条记录</b></p>

    <table class="table table-striped">
        <thead>
            <tr>
                <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><a href={{item.url}}>{{item.title}}</a></td>
            <td>{{item.author}}</td>
            <td>{{item.keywords}}</td>
            <td>{{item.source_name}}</td>
            <td>{{item.publish_date}}</td>
        </tr>

        </tbody>
    </table>
</div>

·

图表展示

以下我们一共实现了4个图表,通过导航栏中下拉菜单来选择目标图像,并用 isShowisShow2 标签来控制同在 news.html 页面的各图表的显示。
·

柱状图

在这里插入图片描述
展示了 新闻发布数 随时间的变化情况。
·

饼图

在这里插入图片描述
由于作者数目过多,这里仅展示了新闻发布数在10篇以上的作者分布的饼图。之前提到的对爬虫进行优化以得到“干净”的作者名能方便此处饼图的展示。(虽然也可以在展示饼图时对不干净的作者名进行处理,但当用户数目以及页面访问次数增多时,会带来不必要的开销,因此我们追求在初始时就将干净的数据存入数据库。)
·

折线图

这里对折线图的实现要稍复杂,需要用户现在搜索框中输入想要查询的词语,再处理显示对应词语在不同时间的新闻中出现的次数图。
在这里插入图片描述
public/new.html:

<li class="dropdown">
    <a href="#" class="dropdown-toggle" data-toggle="dropdown">图片<span class="caret"></span></a>
    <ul class="dropdown-menu">
        <li><a ng-click="histogram()">柱状图</a></li>
        <li><a ng-click="pie()">饼状图</a></li>
        <li><a ng-click="word_freq()">折线图</a></li>
        <li><a ng-click="wordcloud()">词云</a></li>
    </ul>
</li>

<!--    所有的图片都绘制在main1位置-->
<span ng-hide="isShow" id="main1" style="width: 1000px;height:600px;position:fixed; top:70px;left:80px"></span>

<!-- 折线图搜索框显示 -->
<div ng-hide="isShow2" id="main1" style="width: 1000px;height:600px;position:fixed; top:70px;left:80px">
    <div class="row" style="margin-bottom: 10px">
        <div class="col-lg-2">
            <input type="text" class="form-control" placeholder="查询词语" ng-model="$parent.word_search">
        </div>
        <div class="col-lg-2">
            <button type="submit" class="btn btn-default" ng-click="wordSearch()">查询</button>
        </div>
    </div>
</div>

public/javascripts/news.js:

$scope.word_freq = function () {
    $scope.isShow = true;  // 调用wordSearch()后再显示
    $scope.isShow2 = false;
};

$scope.wordSearch = function () {
    $scope.isShow = false;

    var word_search = $scope.word_search;
    var word_url = `/news/wordSearch?word_search=${word_search}`;
    $http.get(word_url).then(
        function (res) {
            if (res.data.message == 'data') {
                var myChart = echarts.init(document.getElementById("main1"));
                option = {
                    title: {
                        text: '"' + word_search + '"该词在新闻中的出现次数随时间变化图',
                        x: 'center'
                    },
                    xAxis: {
                        type: 'category',
                        data: Object.keys(res.data.result)
                    },
                    yAxis: {
                        type: 'value'
                    },
                    series: [{
                        data: Object.values(res.data.result),
                        type: 'line',
                        itemStyle: { normal: { label: { show: true } } }
                    }],
                };

                if (option && typeof option === "object") {
                    myChart.setOption(option, true);
                }
            }
        }, function (err) {
            $scope.msg = err.data;
        });
};

routes/news.js:

router.get('/wordSearch', function(request, response) {
    if (request.session['username']===undefined) {
        response.json({message:'url',result:'/index.html'});
    }else {
        var keyword = request.query["word_search"];  // 前端提交传入的搜索词
        var fetchSql = "select content,publish_date from fetches where content like'%" + keyword + "%' 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:myfreqchangeModule.freqchange(result, keyword)}));
            response.end();
        });
    }
});

·

词云

在绘制词云时,我们调用了 nodejieba 库,通过 extract() 方法抽选出每篇新闻中排名前20的词语,合并作为词云的数据集。
由于nodejieba没有去除停用词的方法,人工去除较为麻烦,故用 extract 能较好地避免展示的数据中高频词多为停用词的情况。
在这里插入图片描述

·

用户管理

鉴于对用户相关的操作较多,我们单独新建 userSQLMap.js 文件来记录所需SQL命令,解耦合便于操作。

var userSqlMap = {
    add: 'insert into user(username, password, power, state) values(?, ?, \'normal\', \'normal\')',  //注册时用,默认非管理员
    getByUsername: 'select username, password from user where username = ?',  //登陆时用
    getPower: 'select power from user where username = ?',  // 查询该用户是否是管理员
    getAllUsers: 'select * from user',  // 查询所有用户
    freezeUser: 'update user set state = \'freeze\' where username = ?',  // 冻结用户
    unfreezeUser: 'update user set state = \'normal\' where username = ?',  // 解冻用户
    powerUser: 'update user set power = \'super\' where username = ?',  // 设置用户为管理员
    unpowerUser: 'update user set power = \'normal\' where username = ?',  // 取消用户管理员权限
    getHistory: 'select * from user_action where username = ?',  // 获取用户历史记录
    getState: 'select state from user where username = ?',  // 查询用户状态
};

module.exports = userSqlMap;

·
同样,我们通过点击导航栏“账号管理”下拉菜单中的“管理”选项可以跳转管理界面。但此处仅对 管理员 身份用户有效,若为普通用户则点击无用,会跳转会新闻查询界面。

// 管理用户
router.get('/manage', function (req, res, next) {
  var user = req.session;
  if (user['username'] === undefined) {
    response.json({ message: 'url', result: '/index.html' });
  } else {
    userDAO.getPower(user.username, function (powerflag) {
      // 返回的powerflag为:[ RowDataPacket { power: 1 } ]
      if (powerflag[0].power != 'super') {
        // res.json({ msg: '您并非管理员,暂无该权限' });
        res.json({ msg: 'url', result: '/news.html' });
      } else {
        res.json({ result: '/manage.html' });
      }
    })
  }
});

·
我们利用 ng-showng-hide 设置标签来美化页面布局。通过单击 查询用户 按钮来展示所有用户,以及他们的身份(管理员与否)、账户状态(冻结or正常)。
还可通过输入用户名,来调整该用户的权限和状态信息。
在这里插入图片描述
·
此外,我们也支持通过输入用户名来查询其相应历史记录。鉴于用户的记录数量一般较大,我们在此处也依旧设置了分页显示结果,每页最大记录数为10。
在这里插入图片描述

·
鉴于该项目代码较多,博客中指贴出小部分做示例,完整代码详见:GitHub——Web Programming

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值