一板一眼就会滋生弱点。这份工作,不是为业余人士而准备的。自负会让每个人都屈膝下跪。要么打,要么跑,优柔寡断令人厌恶。为仆则忠,为主则殆,这便是道德。合适的话语胜过锋利的刀子。

_ A maoのBlog

结对作业二

结对作业二

结对成员 221801202毛富林、221801211宋方滐
这个作业属于哪个课程 2021春软件工程实践|S班 (福州大学)
这个作业要求在哪里 结对作业二
这个作业的目标 对已爬取的论文进行基本操作
其他参考文献 万能的度娘、构建之法、哔哩哔哩

目录

git仓库及代码规范

git仓库:点这里

代码规范:点这里

云服务器:点这里(从封面开始展示)

(tips:如果链接出现了问题,请直接联系学号221801202或者221801211,因为我们对价格高达28元的阿里云服务器不熟练,不知道会出现什么问题)

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 20
• Estimate • 估计这个任务需要多少时间 20 20
Development 开发 2100 2460
• Analysis • 需求分析(包括学习新技术) 640 720
• Design Spec • 生成设计文档 60 60
• Design View • 设计复审 90 90
• Coding Standard • 代码规范(为目前的开发制定合适的规范) 40 40
• Design • 具体设计 100 120
• Coding • 具体编码 810 980
• Coding Review • 代码复审 60 90
• Test • 测试(自我测试,修改代码,提交修改) 300 360
Reporting 报告 300 360
• Test Repor • 测试报告 200 240
• Size Measurement • 计算工作量 60 60
• Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 40 60
合计 2420 2840

成品展示

  • 基础功能部分

    • 整体展示

      • 从封面开始简单地描述,点击Learn about进入首页。首页上方是导航栏,起初是想将对论文所有的操作分布在导航栏上,但经讨论后觉得完全没必要,所以导航栏就是简单的风格,后续如有需要,会修改、增加功能。接着是热词走势图,然后用一个表格展示论文简要信息,考虑到标题、摘要、关键词等篇幅过长等整体感官的影响,因此我们决定当文本超出限制时就显示省略号或者hidden,此时可以点击“查看”按钮,阅读详细信息。
      • 效果展示如下
    • 论文查看、删除

      • 点击删除,会删除数据库里的相应记录;点击论文列表里的查看,可以跳转到页面,依次展示论文标题,所属的会议名称,会议的年份,关键词,在正文部分显示内容摘要,并在结尾附上原文的的链接(我们测试过,想要进这些外网的链接有点看运气,大多时候都能进去的),最后点击按钮返回到论文列表页面。
      • 效果展示如下
    • 论文搜索

      • 按照正常人类的搜索习惯,我们实现了精准搜索和模糊搜索:(where paper_tile="xxxxxx")——输入完整的论文标题即可精准查询,或者(where paper_title like 'xxx%')——在表里匹配前几个字符,实现模糊查询。
      • 效果展示如下
    • 标签云及关键词图谱

      • 点击导航栏的关键词图谱可以跳转,在新的页面可以查看由10个关键词形成的标签云和一张根据关键词数量形成的条形图。标签云里的关键词依据数量而获得权重(weight),数量越多,权值越大,标签也就越大,点击标签云里的关键词也可以跳转到首页,并且下方的表格会显示所点击关键词的所有记录。
      • 效果展示如下
  • 附加部分

    • 爬虫的初步实现

      • 这个真的很难。。。。。。不过我们也朝着大致的方向前进,比如:对外网的论文爬取实在是太难实现,因为那些老师、助教给网站是Javascript解析的,而且外网爬起来很慢。基本我们能力的局限性,我们只好把目光转向国内的网站。目前,基本的功能是能够完成的,爬取的网站用HTML和JSON两种方式保存(title是键,后面的是值)。

      • 效果展示如下

      • 命令行输出展示

      • 爬取的JSON文件

      • 爬取的HTML

    • 对论文列表的分页功能

      • 具体效果见上述GIF图
      • 这是一个头疼的问题,因为老师、助教给的论文json文件,解析完后发现竟然多达万数行,没部署在服务器上的时候,加载慢的要死,还经常卡死,因此表格的行数会在一页中一直增加下去。所以我们干脆就限制每页的行数,并实现分页,这样的话加载起来就没那么卡了。(任然存在的小问题:每次重新加载的时候还是有一点慢,部署在云服务器上感觉还不错,虽然是价格高达28的阿里云服务器,1G的。)

设计实现过程

思维导图:

  • 如下展示的是我们起初的设计导图。

数据库表格

  • 解析完成后的数据库。

    前、后端项目

  • 前端项目

  • 后端项目

代码说明

后端

Keynum类(存放关键词和次数)

public class Keynum {
    private String keyword;
    private int appeartimes;

Paperinfo(存放论文信息)

public class PaperInfo {
    private String title;
    private String year;
    private String meeting;
    private String abstr;
    private String url;
    private String keyword;

PaperInfoDao实现类(用于实现与paper表和name_keyword表的连接操作)。

定义了添加关键词的方法“addwordkey”。

public class PaperInfoDAOImpl implements PaperInfoDAO {
    public String addwordkey(PaperInfo paperInfo,String str) {
        String sql = "select * from name_keyword where name = \"" + str + "\"";
        String temp = "";
        try {
            connection = DBUtil1.getConnection();   //待优化
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                temp += (resultSet.getString("keyword") + ",");	//将所有找到的关键词通过逗号连接
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return temp;
    }

展示表格的方法“selectPaperInfos”

    public PaperInfo selectPaperInfo(String str) {      //展示单个页面
        String sql = "select * from paper where name = \"" + str + "\"";
        PaperInfo paperInfo = new PaperInfo();
        try {
            connection = DBUtil1.getConnection();   //优化
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                paperInfo.setTitle(resultSet.getString("name"));
                paperInfo.setYear(resultSet.getString("year"));
                paperInfo.setMeeting(resultSet.getString("meeting"));
                paperInfo.setAbstr(resultSet.getString("abstract"));
                paperInfo.setUrl(resultSet.getString("url"));
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil1.close(resultSet,preparedStatement,connection);
        }
        String string = addwordkey(paperInfo,str);
        paperInfo.setKeyword(string);
        return paperInfo;
    }

模糊查询的方法“getinfo”

    public List<PaperInfo> selectPaperInfos() {     //展示列表
        List<PaperInfo> list = new ArrayList<PaperInfo>();
        String sql = "select * from paper"+ " limit 1000";		//限制显示1000行数据
        try {
            connection = DBUtil1.getConnection();   //待优化
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                PaperInfo paperInfo = new PaperInfo();
                paperInfo.setTitle(resultSet.getString("name"));
                paperInfo.setYear(resultSet.getString("year"));
                paperInfo.setMeeting(resultSet.getString("meeting"));
                paperInfo.setAbstr(resultSet.getString("abstract"));
                paperInfo.setUrl(resultSet.getString("url"));
                paperInfo.setKeyword("hidden...");
                list.add(paperInfo);
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil1.close(resultSet,preparedStatement,connection);
        }
        return list;
    }

展示表格的方法“selectPaperInfos”

    public List<PaperInfo> getinfo(String title) {  //模糊查询
        List<PaperInfo> list = new ArrayList<PaperInfo>();
        String sx = "'"+title+"%'";	//设置可以自动搜索带有title的论文
        String sql = "select * from paper where name like " + sx;	
        try {
            connection = DBUtil1.getConnection();   //待优化
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                PaperInfo paperInfo = new PaperInfo();
                paperInfo.setTitle(resultSet.getString("name"));
                paperInfo.setYear(resultSet.getString("year"));
                paperInfo.setMeeting(resultSet.getString("meeting"));
                paperInfo.setAbstr(resultSet.getString("abstract"));
                paperInfo.setUrl(resultSet.getString("url"));
                paperInfo.setKeyword("hidden...");
                list.add(paperInfo);
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil1.close(resultSet,preparedStatement,connection);
        }
        return list;
    }

通过标签云显示对应列表的方法“getkey”

    public List<PaperInfo> getkey(String keyword) {
        List<PaperInfo> list = new ArrayList<PaperInfo>();
        String sql = "select * from paper,name_keyword where name_keyword.keyword = \"" + keyword + "\"" + "and paper.name = name_keyword.name";	//通过两张表连接搜寻
        String name =null;
        try {
            connection = DBUtil1.getConnection();   //待优化
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                PaperInfo paperInfo = new PaperInfo();
                paperInfo.setTitle(resultSet.getString("name"));
                paperInfo.setYear(resultSet.getString("year"));
                paperInfo.setMeeting(resultSet.getString("meeting"));
                paperInfo.setAbstr(resultSet.getString("abstract"));
                paperInfo.setUrl(resultSet.getString("url"));
                paperInfo.setKeyword(resultSet.getString("keyword"));
                list.add(paperInfo);
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil1.close(resultSet,preparedStatement,connection);
        }
        return list;
    }

删除数据的方法“delete”

    public void delete(String title) {
        String sql = "DELETE FROM paper WHERE name =\""+title+"\"";	//连同表中数据一起删除
        try {
            connection = DBUtil1.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.execute();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil1.close(resultSet,preparedStatement,connection);
        }
    }
}

KeynumDao类(实现了与keywords表的连接,用来实现与该表的各种操作)
以下方法操作与上类似,代码展示略

public List<Keynum> selectkey();	//查找前十个最火的关键词
public List<Keynum> selectyear(String year, String meeting)	//通过年份和会议名查找最火的十个关键词

Servlet类

  • UserServlet(通过setAttribute设置键值对的方式,将数据传到首页)

      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            dao = new PaperInfoDAOImpl();
            keynumDao = new KeynumDaoImpl();
            List<PaperInfo> paperInfos = dao.selectPaperInfos();
            if(req.getParameter("search")!=null)		//查询
            {
                paperInfos = dao.getinfo(req.getParameter("search"));
                req.setAttribute("paperInfos",paperInfos);
                req.getRequestDispatcher("/front/index.jsp").forward(req,resp);
            }
            else if(req.getParameter("delete")!=null)		//删除
            {
                dao.delete(req.getParameter("delete"));
                List<PaperInfo> paperInfos1 = dao.selectPaperInfos();
                req.setAttribute("paperInfos",paperInfos1);
                req.getRequestDispatcher("/front/index.jsp").forward(req,resp);
            }
            else if(req.getParameter("p")!=null)		//关键词查询
            {
                paperInfos = dao.getkey(req.getParameter("p"));
                req.setAttribute("paperInfos",paperInfos);
                req.getRequestDispatcher("/front/index.jsp").forward(req,resp);
            }
            else if(req.getParameter("view")!=null)		//论文信息展示
            {
                req.setAttribute("paperInfo",dao.selectPaperInfo(req.getParameter("view")));
                req.getRequestDispatcher("/front/user.jsp").forward(req,resp);
            }
            else		//展示线性表和所有数据
            {
                List<Keynum> list = new ArrayList<Keynum>();
                list = keynumDao.selectyear("2020" , "cvpr");
                for(Keynum keynum : list)
                {
                    System.out.println(keynum.getKeyword());
                }
                req.setAttribute("cvpr",keynumDao.selectyear("2020" , "cvpr"));
                req.setAttribute("paperInfos",paperInfos);
                req.getRequestDispatcher("/front/index.jsp").forward(req,resp);
            }
    }
    
    • MyServlet(和论文展示页对应)
    • ChildServlet(和关键词展示页对应)

Filter(过滤器,将页面解码为UTF-8)

public class EncodingFilter {
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        request.setCharacterEncoding("UTF-8");
        filterChain.doFilter(request,response);
    }

    public void destroy() {

    }
}

前端

展示表格(通过JSP来展示表格,并实现分页)
<% List<PaperInfo> list= (List<PaperInfo>) request.getAttribute("paperInfos"); %>
<% int i=0;%>
<% for(PaperInfo user : list){ %>
<tr>
    <td><div class="absrtr"><%=user.getTitle()%></div></td>
    <td><%=user.getYear()%></td>
    <td><%=user.getMeeting()%></td>
    <td><div class="abstract"><%=user.getAbstr()%></div></td>
    <td><%=user.getUrl()%></td>
    <td><%=user.getKeyword()%></td>
    <td>
        <form action="homepage" method="get">
            <input type="hidden" name="view" value=
                    "<%=user.getTitle()%>"
            >
            <input type="submit" value="查看" />
        </form>
    </td>
    <td>
        <form action="homepage" method="get">
            <input type="hidden" name="delete" value=
                "<%=user.getTitle()%>"
            >
            <input type="submit" value="删除" />
        </form>
    </td>
</tr>
<% } %>

通过表单实现搜索

<form class="navbar-form navbar-left" role="search">
    <div class="form-group">
        <input type="text" class="form-control" name="search"/>
    </div> <button type="submit" class="btn btn-default">搜索</button>
</form>

删除和查找同上,通过表单实现
通过bootstrap,jquery,echart,highchart来实现标签云,条形图和走势图,数据部分同样使用了jsp来进行前后端交互

<script src="<%= basePath+"javascript/jquery-1.8.3.js" %>"></script>
<script src="<%= basePath+"javascript/highchart.js" %>"></script>
<script src="<%= basePath+"javascript/exporting.js" %>"></script>
<script src="<%= basePath+"javascript/Jcloud.js" %>"></script>
<script src="<%= basePath+"javascript/bootstrap.js" %>"></script>
<link rel="stylesheet" href="<%= basePath+"css/cloud.css" %>" type="text/css">
<link rel="stylesheet" href="<%= basePath+"css/style1.css" %>" type="text/css">

<script src="<%= basePath+"javascript/chart.js" %>" style="width: 50%"></script>
                        <script style="width: 50%">
                            var data = [
                                <% List<Keynum> list= (List<Keynum>) request.getAttribute("keynumList"); %>
                                <% for(Keynum keynum : list){ %>
                                {xAxis:'<%=keynum.getKeyword()%>',value:<%=keynum.getAppeartimes()%>},
                                <% } %>
                            ]
                        </script>

附加功能

#### 爬虫

爬虫的实现(只能爬取由html构成的网站,对助教给的那三个网站爬去不了,但是学了很久,又不想放弃,就写在这里了。。。)

Spider.create(new MyProcesser())
        //.addUrl("https://dblp.uni-trier.de/db/conf/eccv/eccv2020-1.html")
        .addUrl("https://cnblogs.com/")		//爬取csdn
        .addPipeline(new ConsolePipeline())	//爬取试着
        .addPipeline(new JsonFilePipeline("e:/WebLib"))	//保存为jaso文件
        .addPipeline(new FilePipeline("e:/File"))	//保存为html文件
        .addPipeline(new MyPipeline())	//过滤
        .run();	//运行

选择爬取网站的格式,定位

public void process(Page page) {
    //System.out.println(page.getHtml().toString());               //获得网站的H5
    //page.addTargetRequests((page.getHtml().links().all()));     //爬取全部内容
    page.addTargetRequests(page.getHtml().links().regex("https://www.cnblogs.com/[\\w\\-]+/p/[0-9]+.html").all());    //博客园
    page.putField("title",page.getHtml().xpath("//*[@id=\"cb_post_title_url\"]/span"));				//通过xpath来定位要查找的数据
    //page.addTargetRequests(page.getHtml().links().regex("https://link.springer.com/chapter/[0-9\\-\\%\\.]+.html").all());


}

public Site getSite() {
    return Site.me().setSleepTime(100).setRetrySleepTime(3);
}

连接池(DBCP连接池)

这也是一个半成品的代码,明明没有报错,在别的程序上也能实现,可是在这里就不行了,最后本来想改改看,可是因为本人实力不足,外加同伴没有经验,虽然去专门学习了可是也只能到最后的半成品。

static String driver = "com.mysql.jdbc.Driver";
static String ip="rm-2zeo875ys8j8pq6epuo.mysql.rds.aliyuncs.com";    //IP名字
static int port=3306;      //端口
static String database = "sfjteam";    //数据库名字
static String encoding = "UTF-8";
static String loginName = "wzn";
static String password = "Ww123456";
static int firSize = 10;    //初始化线程池大小
static int maxId = 20;      //最大空闲数量
static int minId = 5;       //最小空闲数量
static int maxAct = 50;   //最大活跃数
static int maxWait = 1000;  //最长等待时间
	
public static void init(BasicDataSource basicDataSource)	//初始化basicDataSource
{
    String url = String.format("jdbc:mysql://%s:%d/%s?characterEncoding=%s&serverTimezone=GMT", ip, port, database, encoding);
    basicDataSource.setDriverClassName(driver);
    basicDataSource.setUrl(url);
    basicDataSource.setUsername(loginName);
    basicDataSource.setPassword(password);
    basicDataSource.setInitialSize(firSize);
    basicDataSource.setMaxIdle(maxId);
    basicDataSource.setMinIdle(minId);
    basicDataSource.setMaxTotal(maxAct);
    basicDataSource.setMaxWaitMillis(maxWait);
}

public Connection getConn(BasicDataSource basicDataSource)
{
    Connection conn = null;
    init(basicDataSource);
    try{
        conn = basicDataSource.getConnection();
    } catch (SQLException e){
        e.printStackTrace();
    }
    return conn;
}


/*public static void main(String[] args) throws SQLException {
    BasicDataSource basicDataSource= new BasicDataSource();
    DBUtil dbUtil = new DBUtil();
    Connection connection = dbUtil.getConn(basicDataSource);
    String sql = "select * from books";
    PreparedStatement preparedStatement = null;
    try{
        preparedStatement = connection.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();
        while(resultSet.next()){
            //String title = resultSet.getString(2);
            //double price = resultSet.getDouble(3);
        }
        if(connection !=null){
            connection.close();
        }
    } catch (SQLException e){
        e.printStackTrace();
    }
}*/

结对讨论过程

  • 刚得知结对课题时,我们就进行了分工,采用交叉合作的方式,这样的好处在于,当自己陷入bug而头脑发昏的时候,另一个清醒的头脑会发挥极大效益。

  • 因为是结对完成课题,交流讨论是必然的,但是大伙儿又都认识,所以比如在QQ、微信上扣文字、发语音,哪里有在宿舍、自习室、空教室里交流来得自来?因此,线上的交流就没有线下来的好,于是我们就放了平时交流时被“偷拍”的照片。

  • 小宋和小毛一起讨论
    -

  • 小毛和小宋一起Debug

心路历程和收获、队友评价

心路历程:最开始时候一直担心写不完,因为上学期的JavaWeb很水(大家都这么觉得),我们对web独立开发也算是第一次:但是在对JSP,servlet进行深入学习后,我们发现JavaWeb是可以实现的,但是可能很难;在结束前一周左右时,写了一个小的Javaweb项目,发现已经有了思路;但是这时候因为太过夸大,也导致了爬虫技术和数据池的半成(虽然学习这东西花了我3天的时间,可能咱俩太笨了。。);但是当作业的完成,我们心中只有满满的成就感,兴奋之情溢于言表。

  • 小宋的收获:这一次掌握了JavaWeb的编程技术,可以让我以后在学习各种Web框架的时候更加轻松,当然,对项目的时间管理上又获得了经验。
  • 小毛的收获:这一次终于明白了前端对界面的强迫症,那是改了又改呐,留下了一大盆泪水,不过终于搞好了!同时,学会了许多非常超级实用的知识,总而言之就是,我很期待下一次的表现!

小宋:在和毛sir交流后,他表现出来了平时没有的执行力,虽然他的编程能力不强,但是他对界面的设计和对新知识的学习能力还是值得信赖的,我感觉下次实践我可以和他做出更棒的项目。

小毛:宋sir真乃奇才也!俺们发现只要我们付出努力、拼搏,就不知不觉解决了好多问题啊!我们的合作将会越来越强大!

posted @ 2021-03-31 22:56  蒲公英吹啊吹  阅读(120)  评论(10编辑  收藏  举报