福大软工1816 · 第五次作业 - 结对作业2

福大软工1816 · 第五次作业 - 结对作业2

分工以及代码规范

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 30
· Estimate · 估计这个任务需要多少时间 30 30
Development 开发 690 960
· Analysis · 需求分析 (包括学习新技术) 30 240
· Design Spec · 生成设计文档 30 30
· Design Review · 设计复审 20 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 20
· Design · 具体设计 30 30
· Coding · 具体编码 360 450
· Code Review · 代码复审 30 30
· Test · 测试(自我测试,修改代码,提交修改) 180 140
Reporting 报告 110 80
· Test Repor · 测试报告 30 20
· Size Measurement · 计算工作量 20 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 60 40
合计 830 1070

解题思路描述与设计实现说明

爬虫的使用

查看了C++的爬虫,差不多是基于WinSocket拿到HTML信息,爬下来再去分析即可,工作量可能不小。

负责爬虫的队友国庆期间事情有点多,最后没有使用C++来实现爬虫

  • 爬虫环境:Python3.6 Windows 10
  • 爬虫代码链接
  • 使用的库包括requestsbeautifulsoup,其中requests库用来爬取HTML信息,beautifulsoup库用来美化HTML信息并从HTML中查找标签信息
  • 针对CVPR官网网络IO较慢,容易造成爬取中断的问题,使用及时存储方法, 大大增加了爬虫的健壮性
  • 考虑到CVPR多个论文中存在非ASCII码(主要是一些拉丁文)的问题,使用UTF-8编码保存相关信息,解决了部分拉丁文乱码问题

代码组织与内部实现设计(类图)

应作业要求,本次代码组织如下

原谅本人并不认为文件越多就越好,我们的文件虽然少但结构还算清晰(#.#)

031602345&031602321
|- src
    |- WordCount.sln
    |- WordCount
        |- stdafx.h             预编译头文件
        |- WordCount.h          定义外部接口
        |- WordCount.cpp        main函数入口
        |- function.cpp         程序内函数的实现
        |- stdafx.cpp           预编译文件
        |- WordCount.vcxproj
|- cvpr
    |- Crawler.py
    |- result.txt

程序调用结构如图

  • 对程序执行过程的解释:
    1. 主程序首先执行参数解析函数
    2. 执行GetWordCountMap()函数,获得词频统计字典。为了减少不必要的文件IO,故在在获取词频统计字典的过程中顺路得到到characters、words、lines信息,并修改g_has_got_mapg_has_got_linesg_has_got_wordsg_has_got_characters四个标志位为True
    3. 由于在获取词频字典的时候已经修改了标志位,程序接下来可以直接获取到words、characters、lines信息,而不需要再读取文件
    4. 执行GetFirstNWords()函数,获取rank前N的单词
    5. 显示结果(输出到文件中)

说明算法的关键与关键实现部分流程图

这次的重点应该是带有指定长度的词组词频统计功能,剩下的功能只要在上一份代码的接口上做一些改动即可

以下是用于实现词组词频的实现过程

WordCount

附加题设计与展示

附加题1:基于英文分词技术的热词分析功能

  • 独到之处:使用英文分词技术,而不是傻大黑粗的指定长度的分词,尽量保留大家感兴趣的专有名词,方便大家看到年度热词(多为专有名词)

  • 实现思路:使用python的textblob库,对每篇论文分析得到英文词组,同时构建一套简单的规则去除一些常见词,进而得到词频统计。数据展示部分使用了seaborn库的功能

  • 实现成果展示:

附加题2:年度最佳作者排行(基于发布的论文数)

  • 独到之处:你想知道CVPR最大牛的是谁吗?我们可以根据每个作者发表的论文数量,简单分析年度最佳作者。注:能力有限,没有考虑重名的问题(实际上这个问题如果要考虑进去,会变得很复杂)

  • 实现思路:在爬虫中只要把每篇文章的作者跟着爬取下来,根据作者名出现的次数进行排序即可。

  • 实现成果展示:

    附加题3:论文翻译工具(还是个半成品)

    • 独到之处:可以对爬取到的论文进行翻译, 暂时还是个半成品o(╥﹏╥)o

    • 实现思路:这里使用了一个境外服务器来实现翻译

    • 实现成果展示:

关键代码解释

头文件中定义的外部接口一览

其中Init()SetParam()函数是为了能在测试程序中模拟命令行参数而设计的,在程序运行过程中没用

词组词频统计部分(代码注释中有详细说明每一段代码的功能,其中部分代码被折叠)

性能分析与改进

原始的性能分析如图,其中GetFirstWordsGetWordCountMap函数的开销较大

在查看WordCountMap函数的过程中,发现我本来使用的双端队列的push_back很慢啊,想了想当初干嘛要用双端队列呢,好像没啥用(可能是方便吧),想先改用队列来看一下

CPU的总计算量从18588下降到了16728(速度提升10%)

后来又想了想,感觉为啥要用队列呢?直接用两个指针不好吗?然后就开始更改成只用两个指针。不过改的过程中出了BUG,还在修复...等修复完了再更新博客

程序中消耗最大的函数是GetWordCountMap这个家伙

GetWordCountMap中占比最大的函数是AddWordWeight这个函数

AddWordWeight这个函数中,我目前使用的是STL::Map,在考虑自己手写一个字符串哈希来代替Map的功能,这部分在我改进完后再更新博客

单元测试

上次因为自己找了很久没找到我VS中的覆盖率分析工具,也怪自己没看到需要至少10组测试样例,吃了没做好单元测试的大亏。这次重新学习VS中单元测试部分,设计了10组测试。

另外字符统计不应该统计/r,在这个版本中做了修正

测试模块名 测试描述 预期结果
EmptyFileTest 打开一个空文件 字符数、有效行数、单词数均为0
CountCharTest 字符统计测试 字符数中'Title:' 'Abstract: ' 论文编号以及多余换行符不应该被统计
CharTestForCr \r字符测试 \r字符不应该被统计
CountLineTest 统计有效行 只统计Title、Abstract打头的有效行
WordInOtherLineTest 单词统计 用于测试其他行的单词是否被计数 只统计Tiltle、Abstract打头的行内的单词,出现在其它行的单词不予考虑
WordGroupTest 词组判断测试,参数m设置为3来测一个含有4个单词的词组 理论词组的数量为2
MultiListWordGroupTest 用于测试程序能否保存住中间的分隔符 需要保存住中间的分隔符
WordGroupValidTest 词组合法性检查 用于测试中间存在不合法单词时的处理情况 当中间存在不合法单词时不应该组成词组
WeightTest0 用于测试当m设置为1时title的权重是否改变 Title中的词组的权重应该为10
WeightTest1 用于测试当m设置为0时title的权重是否为1 Title中的词组的权重应该为1

测试代码例

// 包含多个分隔符的词组检查
namespace MultiListWordGroupTest
{
	TEST_CLASS(UnitTest1)
	{
	public:

		TEST_METHOD(TestMethod1)
		{
			Init();
			string input_path = "multilistwordgrouptest.txt";

			// 设置词组长度为3
			vector<map<string, int>::iterator> first_n_words = GetFirstNWords(input_path, 3, 10000);

			/*
			multilistwordgrouptest.txt文件中的内容为
			Title: Word1 !  word2 + word3
			*/

			int number_word_groups = first_n_words.size();
			// 理论上有1个词组
			Assert::IsTrue(number_word_groups == 1);

			string word = first_n_words[0]->first;
			int equal = word.compare("word1 !  word2 + word3");
			Assert::IsTrue(equal == 0);
		}
	};
}

代码覆盖率测试

主函数所在的cpp文件的代码覆盖率为100%

用于实现各个函数的具体功能的function.cpp代码覆盖率有点低,仅为71%,虽然不高但完完全全符合我的预期。覆盖率低主要是由于项目机构导致的,在获取完词频统计字典后,用于获取字符、获取有效行、获取单词总数的函数就不必调用了。可以发现在function.cpp中未被调用的大部分还是这部分函数

另外由于init和SetParam函数主要是为了测试方便提供出来的接口,在程序运行过程中也没有被调用,和预期结果差不多(要是被调用了那就有鬼了o(╥﹏╥)o)

Github的代码签入记录

未能做到代码一有变更就立马上传,因为有时候改BUG的时候改入迷就忘了,所以有的迁入是多个功能一起迁入的,下次一定想办法改进!下次考虑给自己列一个列表,今天要改哪些功能,每改进一个功能就在git上迁入一次。

遇到的代码模块异常或结对困难及解决方法

遇到的困难 做过哪些尝试 是否解决 有何收获
用C++写爬虫 查找有没有前辈写过可供参考的文档,看到了winsocket编程,迫于队友较忙个人时间有限,没有使用C++来编写爬虫 未解决 虽然没啥收获但是有感想
重新学习Python爬虫 之前还是写过requests、bs4库的,重新学习了一下 解决 还是python做爬虫容易
爬取过程中由于网络问题爬虫容易失败,失败了又要重来,重来的时候又可能被断掉... 对爬取到的文章做及时存储,已经在本地存在的文章下次就不爬了 解决 增加了点代码量
附加题中的分词 尝试过nltk库、textblob库,最终使用了textblob库 解决 学到了新库的使用
附加题分词中,在已经设置仅获取名词的情况下仍然出现了大量如different、learning这样的常用词 观察了词的结构,发现大部分的专有名词还是多个单词的复合,于是自己设置了一套规则,用于屏蔽differet、learning这样的单词 解决但不完美 自己动手解决问题
附加题中需要做数据展示 接触pandas、matplotlib、seaborn库,学习csv文件的格式 解决但不完美 初步接触python数据分析与展示
使用matplotlib时x轴标签会被挡住 查了matplotlib的文档以及多篇博客,尝试自己设置bottom属性,暂时只能通过手工调整来解决 未解决
使用pyinstaller将python转成exe文件过程中报错 谷歌查了很多,还是未能解决这个问题。我成功用pyinstaller转成功了几个程序,只是我们附加题的py程序,暂时无法转成exe程序。 未解决 第一次知道python可以很方便的转成exe程序

评价我的队友

    • 值得学习的地方:学习优异,性格和善严谨,在学习上非常努力。

个人学习进度条

第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 200 200 10 10 php学习
2 100 300 15 25 学习Axure RP的基本操作
posted @ 2018-10-10 20:34  又是一个晴朗的早晨  阅读(307)  评论(1编辑  收藏  举报