課程 | 軟件工程1916|W(福州大學) |
做業要求 | 結對第二次—文獻摘要熱詞統計及進階需求 |
結對博客 | 221600426 221600401 |
Github基礎需求項目地址 | 221600426 221600401 |
Github進階需求項目地址 | 221600426 221600401 |
做業目標 | 實現一個可以對文本文件中的單詞的詞頻進行統計的控制檯程序,並能在基本需求實現的基礎上,經過爬取CVPR2018官網並進行頂會熱詞統計 |
具體分工 | 221600426負責主要代碼的編寫,221600401負責學習爬蟲和博客的撰寫 |
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
Planning | 計劃 | 2400 | 2650 |
Estimate | 估計這個任務須要多少時間 | 2400 | 2650 |
Development | 開發 | 720 | 900 |
Analysis | 需求分析 (包括學習新技術) | 100 | 150 |
Design Spec | 生成設計文檔 | 60 | 70 |
Design Review | 設計複審 | 60 | 80 |
Coding Standard | 代碼規範 (爲目前的開發制定合適的規範) | 30 | 20 |
Design | 具體設計 | 180 | 220 |
Coding | 具體編碼 | 770 | 850 |
Code Review | 代碼複審 | 120 | 100 |
Test | 測試(自我測試,修改代碼,提交修改) | 120 | 70 |
Reporting | 報告 | 60 | 70 |
Test Repor | 測試報告 | 30 | 40 |
Size Measurement | 計算工做量 | 30 | 25 |
Postmortem & Process Improvement Plan | 過後總結, 並提出過程改進計劃 | 120 | 55 |
合計 | 2400 | 2650 |
首先是語言的選擇,因爲221600426較常作LeetCode題,以及較常使用.net,因此開頭是打算使用C++來開發的,後來較詳細的分析了需求後發現對於此題,咱們可能會須要用到大量的正則表達式以及爬蟲,java有大量的類庫以及前人的經驗,因此最終選擇用java來實現。而後是爬蟲框架的選擇,goole了一下,搜到較多的jsoup實現爬蟲的相關信息,所以咱們就選擇了學習jsoup;分析程序要實現的基本功能,後確認至少須要有三個接口(字符計數,行計數,單詞計數),將其封裝成一個類不妨稱爲WordsHandler,最後針對每個功能進行編碼的實現,以及進行模塊測試,確保獨立模塊穩定,再進行拼接以及總體測試。基本功能實現後就能夠考慮進階需求,先在jsoup Cookbook(中文版)學習jsoup,而後邊學邊寫爬蟲,最後在基礎功能之上經過小修改便可實現進階需求。html
代碼如何組織 :
共需兩個類,一個程序入口Main,一個文本處理WordsHandler。Main實例化一個WordsHandler,該實例在調用其接口charCnt,lineCnt,wordCnt進行字符,有效行數,單詞數統計,最後調用printInfo進行輸出。java
單元測試的設計 :
git
代碼組織與內部實現設計(類圖) :
github
//單詞數統計 public void wordCnt(String line) { String arr[]=line.split("[^a-zA-Z0-9]"); //分割出潛在的單詞 tolowerCase(arr); for(int i=0;i<arr.length;++i) { //System.out.println(arr[i]); if(arr[i].matches("[a-z]{4}[a-z0-9]*")) {//判斷是不是單詞 wordCnt++; if(!wordCntMap.containsKey(arr[i])) { wordCntMap.put(arr[i], 1); }else { wordCntMap.put(arr[i], wordCntMap.get(arr[i])+1); } } } }
代碼如何組織 :
wordCount進階設計了三個類,分別是Main,WordsHandler,Option。Main是程序的入口,實例化一個Option對象,調用該對象的getOption接口獲取操做參數;而後實例化一個WordsHandler對象,調用該對象的readFile讀取文件,並在每讀取一行後調用charCnt,lineCnt,wordCnt進行字符,有效行數,單詞總數統計,若m>1則調用worsCnt進行詞組統計。最後調用writeFile進行輸出。爬蟲採用兩個類,分別是Main,Handler。Main實例化一個Handler,在獲取到目標全部連接後,篩選出具備」content_cvpr_2018/html/「頭部的連接,並使用線程池調用Handle對象的同步方法writeFile進行論文爬取並輸出。正則表達式
單元測試的設計 :
算法
代碼組織與內部實現設計(類圖) :
編程
//單詞組計數 public void wordsCnt(String line) { if(Option.isWeight) {//啓用權值 String letters[]=getWords(line); //分割出潛在的單詞 String separators[]=getSeperator(line);//分割出分隔符 for(int i=0;i<letters.length-Option.m+1;i++) { boolean find=true; for(int j=i,cnt=0;cnt<Option.m;++j,cnt++) { if(!letters[j].matches("[a-z]{4}[a-z0-9]*")) {//判斷是不是單詞 find=false; } } if(find) {//找到連續的m個單詞 String str=letters[i]; for(int j=i+1,cnt=0;cnt<Option.m-1;++j,cnt++) { str+=separators[j-1]+letters[j]; } if(line.contains("Title: ")) { if(!wordCntMap.containsKey(str)) { wordCntMap.put(str, 10); }else { wordCntMap.put(str, wordCntMap.get(str)+10); } } if(line.contains("Abstract: ")) { if(!wordCntMap.containsKey(str)) { wordCntMap.put(str, 1); }else { wordCntMap.put(str, wordCntMap.get(str)+1); } } } } } else {//不啓用權值 String letters[]=getWords(line); //分割出潛在的單詞 String separators[]=getSeperator(line);//分割出分隔符 for(int i=0;i<letters.length-Option.m+1;i++) { boolean find=true; for(int j=i,cnt=0;cnt<Option.m;++j,cnt++) { if(!letters[j].matches("[a-z]{4}[a-z0-9]*")) {//判斷是不是單詞 find=false; } } if(find) {//找到連續的m個單詞 String str=letters[i]; for(int j=i+1,cnt=0;cnt<Option.m-1;++j,cnt++) str+=separators[j-1]+letters[j]; if(!wordCntMap.containsKey(str)) { wordCntMap.put(str, 1); }else { wordCntMap.put(str, wordCntMap.get(str)+1); } } } } }
爬蟲部分的實現採用jsoup,首先爬取http://openaccess.thecvf.com/CVPR2018.py官網的全部a標籤,而後遍歷全部a標籤數組,若該a標籤的href包含「content_cvpr_2018/html/」則它就是具備論文html的頁面,將其徹底限定路徑取出並使用線程池開啓一個線程去爬取論文。線程會調用Handler的同步方法writeFile(該方法共享cnt變量(論文數量),以及文件指針)對目標論文進行按要求爬取。數組
//Main函數部分代碼 try { System.out.println("開始連接"); Document document=Jsoup.connect("http://openaccess.thecvf.com/CVPR2018.py").maxBodySize(0).timeout(1000*60).get(); System.out.println("開始爬取"); handler.writer=new BufferedWriter(new FileWriter("result.txt")); //System.out.println(document.toString()); Elements links=document.getElementsByTag("a"); int cnt=0; for(Element link:links) { String href=link.attr("href"); //System.out.println(href); if(href.contains("content_cvpr_2018/html/")) {//獲取論文 cnt++; cachedPool.execute(new Runnable() { @Override public void run() { // TODO Auto-generated method stub try { //Thread.sleep(100); Document document=Jsoup.connect("http://openaccess.thecvf.com/"+href).maxBodySize(0).timeout(1000*60*5).get(); handler.writeFile(document); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } } System.out.println(cnt); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
class Handler{ BufferedWriter writer; int cnt=0; synchronized void writeFile(Document document) {//目標內容爬取 try { System.out.println(cnt+" "+document.getElementById("abstract").text()); String string=cnt+"\r\n"; string+="Title: "+document.getElementById("papertitle").text()+"\r\n"; string+="Abstract: "+document.getElementById("abstract").text()+"\r\n\r\n\r\n"; writer.write(string); writer.flush(); cnt++; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override protected void finalize() throws Throwable { // TODO Auto-generated method stub super.finalize(); System.out.println("close"); writer.close(); } }
基礎需求的字符,單詞,行數少許計數(有10個測試樣例,這裏只貼出2個)
微信
基礎需求的壓力測試
多線程
進階需求的官網論文測試
1.需求不明確,Edge Case模糊
解決方法 :在羣裏與同窗,助教交流
2.初次使用java爬蟲
解決方法 :上網找教程自學爬蟲
3.結對成員上課時間衝突,未能深刻討論代碼實現
解決方法 :在雙方都沒課時,約個時間討論實現方案;用qq保持交流,實時分享編程進度
4.對於「統計文件的字符數」的功能中一些字符該如何統計未能很好的理解
解決方法:在微信羣和博客中向助教和老師提問直到完全理解需求,對程序根據助教提供的樣例進行測試
隊友積極配合,細心,耐心,具備上進心,兩次做業合做下來很是愉快。 |
個人隊友代碼能力超強!以前只是據說個人隊友隊友是個大佬,結對後才深入體會到他編程的能力,記得今天才剛分好工,次日就完成了基礎的編程,做爲一隊有一種強烈的不能偷懶的想法。個人隊友學習能力也超強,上次做業的墨刀和此次做業的爬蟲,感受很快就能學會而且本身開始作了,對我提出的問題也會認真講解。我但願能學到隊友那超強的編程能力和學習能力,在做業過程當中能不拖累隊友! |