本次做業隊友:
031602502: 結對隊友博客,
同名倉庫Github,
本次做業博客連接css
成員信息html
具體分工java
代碼規範python
PSP表格git
爬蟲使用github
代碼組織與內部實現設計(類圖)正則表達式
關鍵算法及流程圖算法
關鍵代碼解釋數組
設計創意獨到之處緩存
實現思路
實現成果展現
性能分析與改進
單元測試
代碼簽入記錄
遇到的代碼模塊異常或結對困難及解決方法
評價隊友
學習進度條
爬蟲的實現(爬取所須要的資源)
代碼複審、測試(單元測試)
界面的實現(與原型相仿的交互性界面)
基本功能(詞頻統計)代碼的實現
咱們一塊兒制定了代碼規範
結對制定的代碼規範見:
博客戳這裏!
敲重點!
多交流、多溝通是最好的規範。
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 30 | 30 |
· Estimate | · 估計這個任務須要多少時間 | 30 | 30 |
Development | 開發 | 2630 | 3000 |
· Analysis | · 需求分析 (包括學習新技術) | 360 | 480 |
· Design Spec | · 生成設計文檔 | 120 | 120 |
· Design Review | · 設計複審 | 30 | 30 |
· Coding Standard | · 代碼規範 (爲目前的開發制定合適的規範) | 120 | 90 |
· Design | · 具體設計 | 120 | 120 |
· Coding | · 具體編碼 | 1200 | 1500 |
· Code Review | · 代碼複審 | 200 | 120 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 480 | 540 |
Reporting | 報告 | 100 | 130 |
· Test Repor | · 測試報告 | 30 | 30 |
· Size Measurement | · 計算工做量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 過後總結, 並提出過程改進計劃 | 60 | 90 |
· | 合計 | 2760 | 3160 |
map和hash_map的取捨博客在這裏!
- 使用了java實現爬蟲,用python再次爬取數據,將兩者爬取結果進行文本對比,以確保結果正確性。(python僅用來驗證)
- java實現設計思路介紹(如下介紹基本功能的爬蟲實現過程):
- 知識點:
URL(Uniform Resource Location),即Web上的文件提供的惟一地址,能夠叫作統一資源定位器。- 使用的函數方法:
1.使用java.net.URL類的構造方法,爲該文件建立一個URL對象
2.URL類中定義的openStream()方法來打開輸入流和用輸入流
3.BufferedReader 是緩衝字符輸入流。它繼承於Reader。
BufferedReader 的做用是爲其餘字符輸入流添加一些緩衝功能。每次從中讀取一部分數據到緩衝中進行操做。(將網頁html存入,每次讀取一行進行正則匹配獲取全部論文url並存於list中)4.同理使用BufferedWriter來輔助文件寫入
5.使用java.util.regex包進行正則匹配。其中Pattern類下的compile()方法編譯正則表達式,返回正則表達式被編譯後的pattern。
而其中的Matcher類沒有提供什麼靜態方法,經過調用 Pattern 對象的 matcher 方法來得到一個 Matcher 對象。而後使用Matcher對象的.find()函數來判斷是否存在匹配。利用group(i)來返回匹配項(i從1開始)
- 獲取網頁html源碼
URL url=new URL("http://openaccess.thecvf.com/CVPR2018.py"); //創建http鏈接並返回鏈接對象 BufferedReader bufr=new BufferedReader(new InputStreamReader(url.openStream()));
- 匹配論文url的正則表達式(利用標籤惟一肯定):
String mail_regex = "<dt class=\"ptitle\"><br><a href=\"(.*?)\">";
- 傳入網站URL,正則表達式匹配全部論文的URL存下來
- 遍歷全部論文的URL(注意要加上http://openaccess.thecvf.com/),分別進行網頁讀取,正則表達式匹配各個論文的標題和摘要並按題目要求格式寫入txt中
- 注意需用UTF-8格式(論文有各類符號若是用默認編碼格式將產生亂碼)
- 附加功能爬蟲源碼見附錄
String head = "http://openaccess.thecvf.com/"; List<String> list=getMailsByWeb(); File f = new File("./Spider_result.txt"); if (!f.exists()) { f.createNewFile(); } OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(f),"UTF-8"); BufferedWriter writer=new BufferedWriter(write); int num = 0; int flag = 0; for(String mail:list) { mail = head + mail; URL url=new URL(mail); System.out.println(mail); System.out.println("\n"); BufferedReader bufred=new BufferedReader(new InputStreamReader(url.openStream(),"UTF-8")); String title_regex = "<div id=\"papertitle\">\\n(.*?)</div>"; String abstract_regex = "<div id=\"abstract\" >\\n(.*?)</div>"; Pattern p = Pattern.compile(title_regex); Pattern q = Pattern.compile(abstract_regex); String line = null; String content = null; while((line=bufred.readLine())!=null) { content = content + line + "\n"; } Matcher w = p.matcher(content); Matcher n = q.matcher(content); if(w.find()&&n.find()) { if(flag == 1) { writer.write("\n\n\n"); } flag = 1; writer.write(String.valueOf(num)); num++; writer.write("\nTitle: "); writer.write(w.group(1)); writer.write("\nAbstract: "); writer.write(n.group(1)); } } writer.close();
一、爬取了各個論文的屬性以及做者和做者工做單位用於附加功能界面的實現(按論文標題進行了同一論文的合併)。利用了網上github概括整合的論文屬性進行爬取 https://github.com/amusi/daily-paper-computer-vision/blob/master/2018/cvpr2018-paper-list.csv
二、爬取了CVPR2018論文PDF連接和論文網址
一、ArgProcessing() 構造函數:對參數進行處理
二、getArgiInFilePath() 獲取-i後的參數:輸入文件路徑
三、getArgoOutFilePath() 獲取-o後的參數:輸出文件路徑
四、getArgwUseTitleWeight() 獲取-w參數:是否使用標題10倍權重
五、getArgmPhraseLen() 獲取-m參數:短語長度
六、getArgnTopNum() 獲取-n參數:結果所需單詞數
一、readFile() 按照指定格式讀取文件
二、writeResult() 按照指定格式寫文件
一、title 論文標題
二、abstract 論文摘要
三、將論文抽象成一個結構,保證了可拓展性,還能夠加入如論文做者、論文網站、論文PDF鏈接等屬性,還能夠對論文屬性進行枚舉。
一、getCharNumber() 獲取字符個數
二、getWordNumber() 獲取單詞個數
三、getLineNumber() 獲取獲取行數
四、getTopPhrase() 獲取最高頻的n個詞組
五、_getAllPhrase() 獲取全部詞組,用於調試
六、isLetter() 判斷給定字符是否爲字母
七、isNumber() 判斷給定字符是否爲數字
八、calcString() 對給定字符串執行統計功能
九、calcAll() 對全部文本內容fileContent執行統計功能
一、定義單詞緩存wordBuf、分隔符緩存separatorBuf、詞組緩存隊列phraseBuf,初始化爲空。
二、對給定字符串進行遍歷,遍歷過程當中記錄單詞緩存、分隔符緩存。
三、遇到分隔符:若單詞緩存中不是單詞,清空分隔符緩存和詞組緩存隊列;若單詞緩存中是單詞,則將分隔符壓入詞組緩存隊列,將單詞緩存壓入詞組緩存隊列。若詞組緩存隊列長度達到指定的詞組長度的2倍(由於壓入的是分隔符和單詞),彈出詞組緩存隊列首個分隔符,遍歷詞組緩存隊列取出詞組,彈出詞組緩存隊列首個單詞。
四、重複步驟3直至處理完整個字符串。
一、對按照以詞組————次數存的map進行n次遍歷
二、每次遍歷,將次數最多的詞組的迭代器取出。經過迭代器將詞組次數置爲相反數(設爲負數,下次尋找最大值排除在外)。將取出的迭代器加入結果數組。
三、取完n個迭代器後,對結果數組進行遍歷,將詞組次數置爲相反數(恢復正數)。完成。
一、以「分隔符-單詞-分隔符-單詞-分隔符-單詞」的形式壓入短語緩存隊列。
二、取短語時,先冒出短語緩存隊列的首個分隔符,而後遍歷隊列取出短語,最後冒出短語緩存隊列的首個單詞。
if (wordBuf.size() >= 4 && isLetter(wordBuf[0]) && isLetter(wordBuf[1]) && isLetter(wordBuf[2]) && isLetter(wordBuf[3])) { m_wordNum++; phraseBuf.push_back(separatorBuf); // 將分隔符壓入詞組緩存 phraseBuf.push_back(wordBuf); // 將單詞壓入詞組緩存 separatorBuf.clear(); wordBuf.clear(); if (int(phraseBuf.size()) == m_phraseLen * 2)//壓入時,單詞、分隔符成對壓入,因此是2倍 { phraseBuf.pop_front(); // 彈出分隔符。 string thisPhrase; for (list<string>::iterator it = phraseBuf.begin(); it != phraseBuf.end(); it++) { // 遍歷詞組緩存隊列,取出詞組 thisPhrase += *it; } m_phraseMap[thisPhrase] += weight; // 在map中進行個數統計 phraseBuf.pop_front(); // 彈出單詞 } }
一、爲確保正確,n應取需求個數n和不一樣短語個數phraseMap.size()的最小值。
二、對phraseMap遍歷n遍,每次遍歷取出最高頻次對應的iterator,加入結果,並經過iterator將詞頻置爲相反數(確保下一次遍歷時不考慮它)。
三、取完n個iterator後,應對取出的iterator再次遍歷,將詞頻置爲相反數,恢復詞頻爲正數。
topNum = min(topNum, int(m_phraseMap.size())); for (int i = 0; i < topNum; i++) { unordered_map<string, int>::iterator maxit = m_phraseMap.begin(); for (unordered_map<string, int>::iterator it = m_phraseMap.begin(); it != m_phraseMap.end(); it++) { if (it->second > maxit->second || it->second == maxit->second && it->first < maxit->first) { maxit = it; } } m_topPhrase.push_back(maxit); maxit->second = -maxit->second; } for (unsigned int i = 0; i < m_topPhrase.size(); i++) { m_topPhrase[i]->second = -m_topPhrase[i]->second; }
說明:成果exe和代碼見附件
熱詞圖譜效果圖(部分):
前10熱詞(包括長度爲2詞組)
前50熱詞(包括長度爲2詞組)
from wordcloud import WordCloud import matplotlib.pyplot as plt f = open(u'Spider_result.txt','r',encoding='utf-8').read() #修改基本功能需求文件內容格式 content = "" i = 0 while(i<len(f)): if f[i]>='0'and f[i]<='9': while(f[i]!='\n'): i+=1 i+=1 while(f[i]!=' '): i+=1 i+=1 title="" while(f[i]!='\n'): title+=f[i] i+=1 i+=1 while(f[i]!=' '): i+=1 i+=1 abstract="" while(i < len(f)): if f[i]=='\n': break else: abstract += f[i] i+=1 i+=3 content+=title + '\n' + abstract + '\n' wordcloud = WordCloud( background_color="white", #設置背景爲白色,默認爲黑色 width=1500, #設置圖片的寬度 height=960, #設置圖片的高度 max_words=50, #設置顯示的熱詞數 margin=10 #設置圖片的邊緣 ).generate(content) # 繪製圖片 plt.imshow(wordcloud) # 消除座標軸 plt.axis("off") # 展現圖片 plt.show() # 保存圖片 wordcloud.to_file('hot words2.png')
1_每日推薦界面
2_論文搜索界面
2_論文搜索界面_搜索功能
3_流行趨勢_十大熱詞排名統計圖
4_人物界面
5_個人收藏界面
6_設置界面
6_設置界面_更改頭像
6_設置界面_更換主題
7_暗黑橙主題_論文搜索界面
7_暗黑橙主題_每日推薦界面
7_清新藍主題_論文搜索界面
7_清新藍主題_每日推薦界面
7_尊貴金主題_論文搜索界面
說明:Debug x86編譯,phraseLen = 1 topNum = 10
起初使用map實現
-改進:用 unordered map 代替 map
傳入(或輸出)文件名 | 測試文件 | 測試函數 | 期待輸出 |
---|---|---|---|
aaa.txt(不存在的文件名) | FileIO.h | FileIO::readFile() | 輸入錯誤的文件名(異常處理) |
empty.txt(傳入)\//\//(輸出) | FileIO.h | File::writeResult() | 輸出文件名錯誤(異常處理) |
無 | ArgProcessing.h | ArgProcessing:ArgProcessing() | 主函數參數過多(異常處理) |
無 | ArgProcessing.h | ArgProcessing::ArgProcessing() | 錯誤命令行參數(異常處理) |
lines(nw)_test.txt | Statistics.h | Statistics::getLineNumber() | 正確統計文本的行數(不帶權重) |
words(nw)_test.txt | Statistics.h | Statistics::getWordNumber() | 正確統計文本中的單詞數(不帶權重) |
char(nw)_test.txt | Statistics.h | Statistics::getCharNumber() | 正確統計字符數(不帶權重) |
TopPhrase_3(nw)_test.txt | Statistics.h | Statistics::getTopPhrase() | 正確輸出頻率前十的詞組(不帶權重) |
Top9Phrase_1(w)_test.txt | Statistics.h | Statistics::getTopPhrase() | 正確輸出頻率前十單詞(帶權重) |
Top2Phrase_3(nw)_test.txt | Statistics.h | Statistics::getTopPhrase() | 正確輸出頻率前n的詞組(不帶權重) |
TEST_CLASS(UnitTestFor_FileIO) { public: TEST_METHOD(TestFor_readFile) { auto fun = [this] { const char* inputfile = "aaa.txt"; const char* outputfile = "result_test.txt"; vector<Paper> fileContent; bool showInConsole = false; FileIO::readFile(fileContent, inputfile, showInConsole); }; Assert::ExpectException<const char*>(fun); } TEST_METHOD(TestFor_writeFile) { auto fun = [this] { const char* inputfile = "empty.txt"; const char* outputfile = "\\//\\//"; vector<Paper> fileContent; int phraseLen = 1; bool useTitleWeight = false; bool showInConsole = false; int topNum = 10; Statistics st(fileContent, useTitleWeight, phraseLen); vector<unordered_map<string, int>::iterator> &topPhrase = st.getTopPhrase(topNum); FileIO::writeResult(1, 1, 1, topPhrase, outputfile, showInConsole); }; Assert::ExpectException<const char*>(fun); } };
TEST_CLASS(UnitTestFor_ArgProcessing) { public: TEST_METHOD(TestFor_ArgProcessingOvermuch) { auto fun = [this] { char *argv[4] = { "WordCount.exe","-i","input.txt","-n" }; int argc = 3; ArgProcessing ap(argc, argv); }; Assert::ExpectException<const char*>(fun); } TEST_METHOD(TestFor_ArgProcessingWrong) { auto fun = [this] { char *argv[13] = { "WordCount.exe","-i","input.txt","-m","3","-n","3","-w","1","-o","output.txt","yeah" }; int argc = 12; ArgProcessing ap(argc, argv); }; Assert::ExpectException<const char*>(fun); } };
TEST_METHOD(TestFor_getTopPhrase_1) { const char* inputfile = "TopPhrase_3(nw)_test.txt"; vector<Paper> fileContent; int topnum = 10; int phraseLen = 3; bool useTitleWeight = false; bool showInConsole = false; FileIO::readFile(fileContent, inputfile, showInConsole); Statistics st(fileContent, useTitleWeight, phraseLen); string answer[10] ={ "abcd_abcd_abcd","abcd_abcd.then", "else_todo_abcd","then_else_todo","todo_abcd_abcd", "abcd.abcd.abcd","abcd.then,else","abcd.then_else", "abcd_abcd.abcd","then,else;todo"}; int frequence[10] = {9,6,6,6,6,3,3,3,3,3}; vector<unordered_map<string, int>::iterator> &tem = st.getTopPhrase(topnum); //FILE *fp = NULL; //fopen_s(&fp, "asd.txt", "w"); for (int i = 0; i < 10; i++) { /*fprintf(fp, "%d\n", tem[i]->second);*/ Assert::IsTrue(tem[i]->first == answer[i] && tem[i]->second == frequence[i]); } }
一、在實現爬蟲的過程當中遇到了亂碼的狀況,經過轉換編碼爲UTF-8解決了這一點
二、在實現爬蟲的過程出現了爬取過程當中get請求某個論文url時超時致使爬蟲卡在那一段,後來利用了設置超時時間來捕獲異常超時處理,異常處理爲從新加載(return)
三、詞組詞頻統計時對於單詞之間分隔符忽略了,致使最後輸出的詞組僅已空格分隔,後來使用了一個詞組緩衝隊列,每次壓入單詞前先壓入分隔符緩存,取出單詞前先冒掉首個分隔符緩存,再遍歷隊列取出詞組,冒掉首個單詞。
一、總是以爲國慶的時間很長,事情留到國慶作,可是假期的效率過低。
二、因爲詞頻統計原先是我的做業,雙方原先代碼風格規範不一致,加上功能模塊較少,協做起來困難,因此在思路共同討論的狀況下分工合做。
值得學習的地方:
一、代碼能力以及代碼的規範程度
二、時間觀念強,很早的完成任務
三、學習能力強,新的東西學習快
須要改進的地方:
睡眠時間要加長
值得學習的地方:
一、強烈的求知慾,刨根問底。
二、學習能力強,學東西快。
三、有良好的編碼規範。
四、思路靈活,富有創造性。
須要改進的地方:
不要熬夜