一、作業基本信息
這個作業屬於哪個課程 | 2021春軟件工程實踐|S班 (福州大學) |
---|---|
這個作業要求在哪里 | 結對作業二 |
結對學號 | 221801205 221801234 |
這個作業的目標 | 1、實現論文網站 2、體會結對編程 3、學會利用Github協作 |
二、主要內容
1、git倉庫鏈接和代碼規范鏈接
1)Github鏈接:https://github.com/gz321/PairProject
2)代碼規范鏈接:https://github.com/gz321/PairProject/blob/main/221801205%26221801234/codestyle.md
2、PSP表格
PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | ||
• Estimate | • 估計這個任務需要多少時間 | 10min | 10min |
Development | 開發 | ||
• Analysis | • 需求分析 (包括學習新技術) | 120min | 120min |
• Design Spec | • 生成設計文檔 | 10min | 10min |
• Design Review | • 設計復審 | 20min | 20min |
• Coding Standard | • 代碼規范 (為目前的開發制定合適的規范) | 20min | 20min |
• Design | • 具體設計 | 20min | 30min |
• Coding | • 具體編碼 | 1800min | 2100min |
• Code Review | • 代碼復審 | 30min | 20min |
• Test | • 測試(自我測試,修改代碼,提交修改) | 120min | 60min |
Reporting | 報告 | ||
• Test Repor | • 測試報告 | 30min | 15min |
• Size Measurement | • 計算工作量 | 10min | 5min |
• Postmortem & Process Improvement Plan | • 事后總結, 並提出過程改進計划 | 120min | 60min |
合計 | 2150min | 2470min |
3、項目訪問鏈接
http://39.102.39.208/crawler_war/static/html/
4、成品展示
- 首頁
- 查詢等待(圖例為點擊關鍵詞圖譜中的關鍵詞后跳轉)
- 查詢
- 表內查詢
- 論文詳情
- 關鍵詞圖譜
- 點擊關鍵詞跳轉
- 熱詞走勢
- 選擇會議
- 顯示某條折線
5、結對討論過程描述
1)討論過程描述:
由於這次作業是上次結對原型作業的后續,因此我們對需求都有了一定了解,在重新看過一遍要求之后,我們分工好了前后端的工作。因為剛上過JavaEE的課程,因此決定用Java實現后端功能,前端則用vue框架實現。我們首先對需求進行解析,分出一個個頁面功能。數據方面我們起先想要用Jsoup和Htmunit實現爬蟲從論文網站上爬取,但中途受阻(難以解析js頁面),於是改為使用助教提供的數據,之后就是一個個功能的實現。
2)結對討論過程截圖:


6、設計實現過程
1)實現過程描述:
后端部分:
首先設計數據庫,在爬蟲受阻后編寫讀取文件代碼,解析json文件並編寫util、bean和dao類,對數據庫進行數據填充,然后根據功能完善dao類,最后編寫與前端進行交互的servlet類,經過在團隊實戰時助教的建議,將數據庫連接信息寫到了配置文件中。
前端部分:
首先根據原型界面設計頁面的基本樣式,使用html與css做出大致頁面,在網絡圖標庫找必要的素材資源。使用echarts與echarts-cloud設計圖表的樣式。使用vue進行頁面動態部分的實現與數據的渲染,使用axios進行前后端的交互。
前后端完成后部署項目,進行bug的修改,刪除不必要的功能,並增加新功能
2)功能結構圖:

7、代碼說明
后端:
1)數據庫設計了三張表:paper、keywords、name_keyword
paper(name,year,meeting,abstract,url,accesstimes):用於存儲論文的主要信息。
keywords(keyword,appeartimes):用於記錄關鍵詞及其出現次數。
name_keyword(name,keyword):由於論文和關鍵詞是多對多關系,需要一張關聯表。
2)JDBCUtil類:用於獲取數據庫連接對象
讀取配置文件信息:
try { pro.load(JDBCUtil.class.getClassLoader().getResourceAsStream("config.properties"));
} catch (IOException e) {
e.printStackTrace();
}
driver = pro.getProperty("driver");
loginName = pro.getProperty("loginName");
password = pro.getProperty("password");
url = pro.getProperty("url");
建立數據庫連接池:池中連接數大於0,則返回池中連接,否則新建連接
if (pool.size() > 0) {
return pool.removeFirst();
} else {
Connection con = null;
try {
con = DriverManager.getConnection(url, loginName, password);
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
3)Servlet類:用於處理前端請求,並回復,請求和回復用json封裝。
取得請求:
String reqString = request.getReader().readLine();
回復:
response.getWriter().write(resString);
其中:
KeywordAndCount類:返回給前端出現次數前十的關鍵詞及其出現次數(用於關鍵詞圖譜顯示)
MeetingWordTrend類:返回給前端三大會議在最后一年里出現次數前三的關鍵詞在三年里的出現次數(用於熱詞走勢的顯示)
PaperListByKeyword類:前端提供關鍵詞,返回前端包含該關鍵詞的論文(用於點擊關鍵詞圖譜,返回論文列表)
PaperListByTitle類:前端提供論文標題,返回前端包含該標題內容的論文(用於論文查詢,支持模糊查詢)
PaperListDetailByTitle類:前端提供論文完整標題,返回前端該論文的完整信息(用於查看文章詳細信息功能)
4)message類:用於處理論文的json文件,將信息存儲到數據庫
因為json文件前后有空格,所以用trim()處理,會議:ICCV和CVPR的json文件最后含有“;”號,無法被JSONObject.parseObject()函數解析,因此用substring取出最后一個“;”。
json = json.trim();
json = json.substring(0, json.length()-1);
5)dao類:對數據庫進行增加和查詢操作。
6)bean類:數據對象類,一張數據表對應一個bean。
前端:
每一個頁面對應一個html一個css和一個js文件,html基本使用bootstarp樣式,js使用vue.js,axios.js,echart.js
- 關鍵代碼
論文列表實現
使用v-for v-model指令,vue通過后端返回的json數組進行數據的渲染
使用兩張圖片,一張藍色,一張灰色做出按鈕的效果
使用@click給圖片添加點擊事件
<table class="table">
<thead>
<tr>
<th>
<span>題名</span>
</th>
<th>
<span>會議名稱</span>
</th>
<th>
<span>發表時間</span>
</th>
<th>
<span>關鍵詞</span>
</th>
<th>
<span>查看</span>
</th>
<th>
<span>刪除</span>
</th>
</tr>
</thead>
<tbody class="listContent">
<tr class="clone1" v-for="todo,index in tp">
<td class="title">{{todo.name}}</td>
<td class="meeting">{{todo.meeting}}</td>
<td class="date">{{todo.year}}</td>
<td class="keyWord">{{todo.keywords}}</td>
<td class="see" v-show="true">
<img alt="" src="../img/see.png" onmouseover="this.src='../img/see_.png'"
onmouseout="this.src='../img/see.png'" @click="send(todo.name)" /></td>
<td class="delete_btn" v-show="true">
<img alt="" src="../img/delete.png" onmouseover="this.src='../img/delete_.png'"
onmouseout="this.src='../img/delete.png'" @click="deleteD(index)"/>
</td>
</tr>
</tbody>
</table>
前端發送請求
采用axios進行異步發送請求
var that= this;
/**
* 訪問路徑 后端端口路徑
* data 向后端發送的數據
* type 設置置HTTP報文請求頭等信息
*/
axios.post(url,data,type).then(function (response) {
//接收response數據,自動轉換為json對象,放入list中
that.list = response.data;
}).catch(function (error) {
//異常處理
console.log(error);
});
頁面跳轉
前端比較簡單,只采用簡單方式進行跳轉
window.location.href = url + "?" + "params" + "=" + params;
window.open(url + "params" + "=" + params);
爬蟲
由於兩個人不熟悉web,沒學過爬蟲,時間比較急,所以只是寫了ICCV和CVPR兩個網站的爬取,使用WebCollector框架
public class AutoNewsCrawler extends BreadthCrawler {
private int id=0;
String s;
int year;
String meeting;
String seed1;
String seed2;
private ArrayList<Paper> papers;//臨時存取爬出的數據
/**
* 構造函數
* @param crawlPath crawl
* @param autoParse true
* @param year 爬取文獻的年份
* @param meeting 爬取的會議名稱
* @param cnt 爬取論文數量
*/
public AutoNewsCrawler(String crawlPath, boolean autoParse,int year,String meeting,int cnt) {
super(crawlPath, autoParse);
this.year=year;
this.meeting="ICCV";
this.seed2="https://dblp.uni-trier.de/db/conf/cvpr/cvpr"+year+".html";
this.seed1="https://dblp.uni-trier.de/db/conf/iccv/iccv"+year+".html";
papers = new ArrayList<>();
if("ICCV".equals(meeting))
this.addSeed(seed1);
else this.addSeed(seed2);
this.addRegex("https://doi.org/[0-9]*.[0-9]*/"+meeting+".[0-9]*.[0-9]*");
getConf().setConnectTimeout(3000);
setThreads(50);
getConf().setTopN(100);
}
@Override
public void visit(Page page, CrawlDatums next) {
String url = page.url();
System.out.println(url);
if (page.matchUrl("https://doi.org/[0-9]*.[0-9]*/" + meeting + ".[0-9]*.[0-9]*")) {
try (BufferedReader reader = new BufferedReader(new StringReader(page.html()))) {
while ((s = reader.readLine()) != null) {
s = s.trim();
if (s.length() != 0 && s.contains("xplGlobal.document.metadata")) {
papers.add(new Paper(s, year, meeting, url));
}
}
} catch (Exception e) {
e.printStackTrace();
}
id++;
if (id > 300) {
this.stop();
}
}
}
/**
* 爬蟲運行方法
* @param year 爬取年份
* @param meeting 爬取會議
* @param cnt 爬取論文數量
* @return 數據列表
* @throws Exception
*/
public static ArrayList<Paper> run(int year,String meeting,int cnt) throws Exception{
AutoNewsCrawler crawler = new AutoNewsCrawler("crawl", true,year,meeting,0);
crawler.start(3);
return crawler.papers;
}
//測試
public static void main(String[] args) throws Exception {
System.out.println(JSONObject.toJSONString(run(2015, "ICCV", 10)));
}
}
由於servlet上不能運行爬蟲(現在還沒找到原因)下面的頁面與servlet的功能在服務器不能體現
前端頁面
seek.html
后端代碼
計划采用將爬到的數據存入文件,通過文件下載的方式獲取
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String reqString = req.getReader().readLine();
JSONObject reqJson = JSONObject.parseObject(reqString);
String s = null;
String meeting = (String) reqJson.get("meeting");
String pwd = (String) reqJson.get("pwd");
int count = (int) reqJson.get("count");
int year = (int) reqJson.get("year");
if(pwd.equals("123456"))
try {
s = JSONObject.toJSONString(AutoNewsCrawler.run(year, meeting, count));
String filename = "paper.txt";
resp.setHeader("content-disposition", "attachment;filename=" + filename);
resp.setContentType("application/octet-stream");
FileOutputStream out1 = new FileOutputStream(this.getServletContext().getRealPath("static/temp/paper.txt"));
out1.write(s.getBytes());
FileInputStream in = new FileInputStream(this.getServletContext().getRealPath("static/temp/paper.txt"));
int len = 0;
byte buffer[] = new byte[1024];
OutputStream out = resp.getOutputStream();
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
//System.out.println(len);
}
in.close();
} catch (Exception e) {
System.out.println("end...");
e.printStackTrace();
}
}
更多相關代碼訪問GitHub
8、心路歷程和收獲
1)221801205:
心路歷程:
剛開始爬蟲功能是由我編寫的,起初看到b站上有關於javaweb利用jsoup爬蟲的視頻教程,跟着學了一會,便開始編碼了,但是讀取到原文網站后,發現網站的信息是使用js動態生成的(后來搭檔做爬蟲時告訴我不用解析js只看html就可以讀取到想要的信息),通過百度知道了Htmlunit可以解析js頁面,但是一直無法成功,網上也沒有完整的教程,想到可能是網站本身的反爬機制,考慮到可能無法爬取,就放棄了爬蟲。再來是關於javaweb,這學期剛開的課,此前還沒有獨立開發過web應用(上學期學的yii框架,只跟着視頻做了一遍,沒有掌握),通過這次的作業將所學應用於實踐,也算有所收獲。
收獲:
實踐了javaweb,了解了一些爬蟲的知識,學會部署服務器,明白自己還有許多做的不夠的地方。
2)221801234:
心路歷程:這次結對作業一開始擔心做不出來,因為我沒寫過前端代碼,隊友也沒寫過后端代碼,花了很多時間進行學習,不過總算是堅持下來了。雖然我們做的十分簡單(簡陋),不過也是這段時間學習web知識后的一次實戰,我還是挺開心的。甚至產生錯覺:感覺審美提升了耶
收獲:這次作業算是小,但是也算是較為完整的web項目。既熟悉了前端的樣式,學習了bootstrap樣式的使用,也了解了前后端交互,感受到了web的獨特魅力,不需要下載軟件,只要一個url就可以訪問。這次結對雖然很累,但是收獲頗豐。痛並快樂着!
9、評價結對隊友
1)221801205:
234的實踐經驗比較多,因為他做后端的經驗較多,所以這次由我擔任后端,然后他去學習了vue框架做前端,在后端編程里我有許多做的不好的地方,他都會補上,比如請求和響應信息的編碼問題,他編寫了filter編碼和解碼,我有不懂的問題也可以請教他,是一位可靠的搭檔。
2)221801234:
205認真嚴謹,在做交互測試的時候后端沒有大問題;平時積極討論,給我提出了很多的前端頁面的建議,使頁面更簡潔方便;積極學習新技術,同時解決了部署,配置等等的難題