本次做业队友:
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)
三、词组词频统计时对于单词之间分隔符忽略了,致使最后输出的词组仅已空格分隔,后来使用了一个词组缓冲队列,每次压入单词前先压入分隔符缓存,取出单词前先冒掉首个分隔符缓存,再遍历队列取出词组,冒掉首个单词。
一、总是以为国庆的时间很长,事情留到国庆作,可是假期的效率过低。
二、因为词频统计原先是我的做业,双方原先代码风格规范不一致,加上功能模块较少,协做起来困难,因此在思路共同讨论的状况下分工合做。
值得学习的地方:
一、代码能力以及代码的规范程度
二、时间观念强,很早的完成任务
三、学习能力强,新的东西学习快
须要改进的地方:
睡眠时间要加长
值得学习的地方:
一、强烈的求知欲,刨根问底。
二、学习能力强,学东西快。
三、有良好的编码规范。
四、思路灵活,富有创造性。
须要改进的地方:
不要熬夜