博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
福大软工1816 · 第五次作业 - 结对作业2
阅读量:4578 次
发布时间:2019-06-08

本文共 12306 字,大约阅读时间需要 41 分钟。

具体分工

  • 031601131 杨喜源:负责WordCount代码编写。
  • 031601232 朱志豪:负责爬虫和附加题编写。

PSP表格

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

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

爬虫使用

本次爬虫采用python完成,代码是自己写的

思路:

爬取[CVPR2018网页](http://openaccess.thecvf.com/CVPR2018.py)内容,用正则表达式将网页内容中论文链接提前到字符串数组中。    遍历数组,在每次访问论文网页的过程中将论文标题和摘要爬取出来。    根据题目要求的输出格式,将数据输出到result.txt文件中。

代码:

from bs4 import BeautifulSoupfrom urllib.request import urlopenimport reimport randomfilename = 'data.txt'base_url = "http://openaccess.thecvf.com/CVPR2018.py"b_url="http://openaccess.thecvf.com/"html = urlopen(base_url).read().decode('utf-8')soup = BeautifulSoup(html, features='lxml')sub_urls = soup.find_all("a", { "href": re.compile("content_cvpr_2018/html/(.)+CVPR_2018_paper.html$")})k=len(sub_urls)print(k)with open(filename,'w',encoding='utf-8') as f:    for i in range (5):        his=sub_urls[i]['href']        url= b_url + his        html2 = urlopen(url).read().decode('utf-8')        soup2 = BeautifulSoup(html2, features='lxml')        sub_urls2 = soup2.find_all("div",id="papertitle")        sub_urls3 = soup2.find_all("div",id="abstract")        j=str(i)        f.write(j)        f.write('\n')        f.write("Title:"+sub_urls2[0].text.lstrip('\n'))        f.write('\n')        f.write("Abstract:"+sub_urls3[0].text.lstrip('\n'))        f.write('\n')        f.write("\n\n\n")`

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

1474224-20181010233332370-2022763824.png

  • main():处理命令行输入的字符串,把处理后参数传递给其他函数。
  • CharNum():统计字符数量。
  • LineNum():统计行数量。
  • WordNum():统计单词数量。
  • Word_Fre():通过主函数传递的-m,-n,-w参数来输出词频前n的词组。

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

由于本次作业与个人作业2的代码部分相同,如判断单词等。这里就不赘述了。

处理命令行输入的字符串

  • 命令行输入的内容传递到 **char *argv[]的二维数组中,通过查找其中的"-i","-o","-m","-w","-n"**等字符串的下一个字符串即为要传入的参数
  • 由于"-i","-o"所传递的本身为字符串,所以无需做特殊处理,记录其下标即可。
  • "-m","-w","-n"所要传递的参数为int型,所以要用atoi()函数将string转换为int
  • 由于"-i","-o","-w"必须传入,所以对未传入这个三个参数的命令行应该报错

部分代码如下:

int topn = 10;    int word_m = 1;    int word_w = 1;    int i = 1;    int infile=-1, outfile=-1;    string in = "-i",o="-o",w="-w",m="-m",n="-n";    while (argv[i])    {        if (argv[i] == in) infile = i + 1;                                                      if (argv[i] == o) outfile = i + 1;                                                      if (argv[i] == w) word_w= atoi(argv[i+1]);        if (argv[i] == m) word_m = atoi(argv[i + 1]);                                           if (argv[i] == n) topn = atoi(argv[i + 1]);        i++;    }

权重的设置和改变 -w

根据论文的爬取结果格式,可以很明显的看出,一篇论文共占用5行,其中第二行"Title: "行第三行"Abstract: "行

  • 对回车数(int huiche)进行统计,则回车数huiche%5==1"Title: "行,回车数huiche%5==2"Abstract: "行
  • 执行完huiche%5==1和huiche%5==2,后将光标移动到“:”冒号后,可以消除Abstract: 和Title: 对统计的影响,同时也可以消除论文编号和Title:行和Abstract:行可能出现的空白行 回车符不被记录,保证一遍论文占用5行。
  • 判断完此时为Title:行还是Abstract:行就可以改变此次的权重了

代码如下:

if (w == 1)    //赋值权重        {            if (ch == '\n')            {                huiche++;                if (huiche % 5 == 1)                {                    while ((ch = fgetc(file)) != ':');                    quanzhong = 10;                }                if (huiche % 5 == 2)                {                    while ((ch = fgetc(file)) != ':');                    quanzhong = 1;                }            }        }

词组统计 -m

如何判断是否为一个单词这里就不重复了,详见

本篇重点介绍如何判断一个词组。

  • 由于实际情况词组出现频率前几都是以空格为分隔符的词组,所以这里将分隔符不是空格的词组忽略
  • 这里使用一个string word_array[20]模拟循环数组来暂时存放单词,用int re_word_num记录一个词组还剩余的单词数
  • re_word_num==0时,表示此时已经记录了m个单词了,就将循环数组中,最后放入的m的单词取出,中间加空格生成词组,并加入map,同时把re_word_num=1表示在来一个单词就可以生成新的词组了。
  • 当遇到一个失败的单词时,re_word_num=m即重新记录单词数量。
  • 最后将map转换为vector,用sort进行排序,输出前n个即可。

代码如下:

string word_array[20];        int wn = 0;         int re_word_num = m;    for (; (ch = fgetc(file)) != EOF;)              //Determine the word and insert map    {                if ('A' <= ch && ch <= 'Z')            ch = ch + 32;        if (flag == 0) {            if (ch >= 'a'&&ch <= 'z') { flag = 1;   word = word + ch; }            else if (ch !=' ') { wn = 0;    re_word_num=m; }        }        else if (flag == 1) {            if (ch >= 'a'&&ch <= 'z') { flag = 2;   word = word + ch; }            else { flag = 0; word = ""; wn = 0; re_word_num = m;}        }        else if (flag == 2) {            if (ch >= 'a'&&ch <= 'z') { flag = 3;   word = word + ch; }            else { flag = 0; word = ""; wn = 0; re_word_num = m;}        }        else if (flag == 3) {            if (ch >= 'a'&&ch <= 'z') { flag = 4;   word = word + ch; }            else { flag = 0; word = ""; wn = 0; re_word_num = m;}        }        else if (flag == 4) {            if (ch >= 'a'&&ch <= 'z' || (ch >= '0'&&ch <= '9')) { word = word + ch; }            else {                word_array[wn % 20] = word;                word_num++;                wn++;                re_word_num--;                word = "";                if (re_word_num == 0)                {                    for (int j = m; j > 1; j--)                        word = word + word_array[(wn - j) % 20]+" ";                    word = word + word_array[(wn - 1) % 20];                    Word_Num_map[word] = Word_Num_map[word] + quanzhong;                    re_word_num = 1;                }                word = "";                flag = 0;                        }        }        if (ch == '\n')//换行初始化。        {            wn = 0;            re_word_num = m;        }        if (w == 1)    //赋值权重        {            if (ch == '\n')            {                huiche++;                if (huiche % 5 == 1)                {                    while ((ch = fgetc(file)) != ':');                    quanzhong = 10;                }                if (huiche % 5 == 2)                {                    while ((ch = fgetc(file)) != ':');                    quanzhong = 1;                }            }        }    }    if (flag == 4) {        re_word_num--;        word_array[wn % 20] = word;        wn++;        if (re_word_num == 0)        {            word = "";            for (int j = m; j > 1; j--)                word = word + word_array[(wn - j) % 30] + " ";            word = word + word_array[(wn - 1) % 30];            Word_Num_map[word] = Word_Num_map[word] ++;        }        }    vector 
Word_Num_vec(Word_Num_map.begin(), Word_Num_map.end()); sort(Word_Num_vec.begin(), Word_Num_vec.end(), CmpByValue()); FILE * stream; freopen_s(&stream, outfile, "a", stderr); if(Word_Num_vec.size()
: %d\n", ss, Word_Num_vec[i].second); //outfile <<"<"<< ss << ">"<<":" << Word_Num_vec[i].second << endl;); } else for (int i = 0; i != n; ++i) { const char *ss = Word_Num_vec[i].first.c_str(); fprintf(stream, "<%s>: %d\n", ss, Word_Num_vec[i].second); } Word_Num_vec.clear(); fclose(file);

算法流程图如下:

1474224-20181011002906295-1473082959.png

附加题设计和展示

附加题及爬虫python代码实现和爬虫数据文本戳

展示发文数量前十的作者:

思路:和前面爬摘要部分一样,用python可以将我们想要的作者名字爬取下来,然后用Count函数返回出现频率最高的十名作者。

结果展示图:(右侧是作者在顶会上发布论文的数量)

1474224-20181010234337560-2098633887.jpg

代码如下:

from bs4 import BeautifulSoupfrom urllib.request import urlopenimport reimport randomfilename = 'author.txt'base_url = "http://openaccess.thecvf.com/CVPR2018.py"b_url="http://openaccess.thecvf.com/"html = urlopen(base_url).read().decode('utf-8')soup = BeautifulSoup(html, features='lxml')sub_urls = soup.find_all("a", { "href": re.compile("content_cvpr_2018/html/(.)+CVPR_2018_paper.html$")})k=len(sub_urls)print(k)with open(filename,'w',encoding='utf-8') as f:    for i in range (k):        his=sub_urls[i]['href']        url= b_url + his        html2 = urlopen(url).read().decode('utf-8')        soup2 = BeautifulSoup(html2, features='lxml')        sub_urls2 = soup2.find_all("div",id="authors")        ls=sub_urls2[0].text.split(";")        f.write(ls[0].lstrip('\n'))        print(i)print('yes')`

将网站中的作者名字爬出

from collections import Counterf=open('author.txt','r',encoding="utf-8")t=f.read()f.close()tx=t.strip()ls=tx.split(",")def counter(arr):    return Counter(arr).most_common(10) # 返回出现频率最高的十个作家author=counter(ls)for i in range(10):    print(author[i])`

将出现频率最高的十个作者打印出

将作者发布论文数量多少用可视化方法来体现

前面已经将论文作者的名字爬下来了,可视化部分用python的wordcloud库可以很轻易地实现这一功能

效果图如下:

1474224-20181010235710560-1674187805.png

代码如下:

import wordcloudf=open('author.txt','r',encoding="utf-8")t=f.read()f.close()tx=t.replace(' ','')ls=tx.split(",")txt=" ".join(ls)w=wordcloud.WordCloud(width=1000,height=700)w.generate(txt)w.to_file("author.png")filename = 'authorall.txt'with open(filename,'w',encoding='utf-8') as f:    f.write(txt)`

将论文下载的pdf链接放到TXT文件中

用户在查看摘要的过程中,如果感兴趣,就可以凭这个链接直接下载论文的pdf或者在线阅读,本功能也可用Excel来存储,更为直观

效果图:

1474224-20181010234513021-1181128431.png

代码如下:

from bs4 import BeautifulSoup`from urllib.request import urlopen`import re`import random`filename = 'data.txt'`base_url = "http://openaccess.thecvf.com/CVPR2018.py"`b_url="http://openaccess.thecvf.com/"`html = urlopen(base_url).read().decode('utf-8')`soup = BeautifulSoup(html, features='lxml')`sub_urls = soup.find_all("a", { "href": re.compile("content_cvpr_2018/html/(.)+CVPR_2018_paper.html$")})`k=len(sub_urls)`with open(filename,'w',encoding='utf-8') as f:`    for i in range (k):`        his=sub_urls[i]['href']`        url= b_url + his`        html2 = urlopen(url).read().decode('utf-8')`        soup2 = BeautifulSoup(html2, features='lxml')`        sub_urls2 = soup2.find_all("div",id="papertitle")`        sub_urls3 = soup2.find_all("div",id="abstract")`        sub_urls4 = soup2.find_all("a", { "href":re.compile("(.)+CVPR_2018_paper.pdf$")})`        j=str(i)`        f.write(j)`        f.write('\n')`        f.write("Title:"+sub_urls2[0].text.lstrip('\n'))`        f.write('\n')`        f.write("Abstract:"+sub_urls3[0].text.lstrip('\n'))`        f.write('\n')`        l=sub_urls4[0]['href'][6:-1]`        link=b_url+l`        f.write("Link:"+link)`        f.write("\n\n\n")`

性能分析与改进

测试使用了977篇论文爬取结果作为输入数,命令行参数为

-i D:\\date.txt -o result.txt -w 1 -m 3 -n 10

结果为:

1474224-20181010233408058-1523283848.png

性能报告如下:

1474224-20181010233420733-801176501.png

1474224-20181010233429599-1533521469.png

显然耗时最大的函数为Word_Fre()进行单词判定和词组输出,所以会比较耗时,可以看出主要耗时在单词判定上,我将map改为unordered_map时间并没有明显提升。

单元测试

设计了十个单元测试样例,被说明测试的函数,构造测试数据见下表:

测试名 单元测试内容 测试模块 结果
UnitTestCharNum 正常文本输入,统计字符数量 CharNum.cpp 通过
UnitTestLineNum 正常文本输入,统计有效行数量 LineNum.cpp 通过
UnitTestWordNum 正常文本输入,统计单词数量 WordNum.cpp 通过
specialfile Abstract : 行为空的文本,有效行数量 LIneNum.cpp 通过
emptya 传入一个空文件,输出为0 CharNUm.cpp LIneNum.cpp WordNum.cpp 通过
twowordnum 两个单词的词组数量 Word_Fre.cpp 通过
emptyinputfile 没有-i参数的传入,错误报告 main.cpp 通过
topntest top3输出 Word_Fre.cpp 通过
weightwordfre 加入权重的单词频率输出 Word_Fre.cpp 通过

运行结果如下:

1474224-20181010233514653-508219732.png

部分代码展示如下:

namespace UnitTestCharNum{           TEST_CLASS(UnitTest1)    {    public:                TEST_METHOD(TestMethod1)        {            char filename[30] = "D:\\demo.txt";            int count = CharNum(filename);            Assert::IsTrue(count == 74);            // TODO: 在此输入测试代码        }    };}namespace UnitTestLineNum{    TEST_CLASS(UnitTest1)    {    public:        TEST_METHOD(TestMethod1)        {            char filename[30] = "D:\\demo.txt";            int count = LineNum(filename);            Assert::IsTrue(count == 2);            // TODO: 在此输入测试代码        }    };}namespace UnitTestWordNum{    TEST_CLASS(UnitTest1)    {    public:        TEST_METHOD(TestMethod1)        {            char filename[30] = "D:\\demo.txt";            int count = WordNum(filename)-2;            Assert::IsTrue(count == 9);            // TODO: 在此输入测试代码        }    };}namespace emptya{    TEST_CLASS(UnitTest1)    {    public:        TEST_METHOD(TestMethod1)        {            char filename[30] = "D:\\demo2.txt";            int count = LineNum(filename);            Assert::IsTrue(count == 1);            // TODO: 在此输入测试代码        }    };}

贴出Github的代码签入记录

1474224-20181010233527419-1211997199.png

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

  • 问题描述:

主要问题是爬虫和词组的判断

  • 做过哪些尝试:

一开始尝试词组打算用个人项目的那个代码,只不过是多循环几次,后来发现单纯的在原来word的基础上增加长度,这样判断词组会有以后,即不能回读。

  • 是否解决:

算是圆满地解决了,通过string数组暂时存放单词,这样就可以在光标移到后面,也可以回读前面的单词。

  • 有何收获:

有,比如软工实践还是要趁早做,就像前面说的,deadline并不是万能的。

还有学习新知识一定要耐心,如何从浩瀚的网络海洋中学到你想要的,这是一门人生的必修课

评价你的队友

我的队友,高端大气上档次,低调奢华有内涵。

  • 值得我学习的地方:做事效率高。
  • 需要改进的地方:不够完美。

学习进度条

第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 300 300 15 15 熟悉了C++语言,了解了单元测试,代码覆盖率和性能分析
2 0 300 8 23
3 300 600 14 37 爬虫,代码能力更上一步
...

转载于:https://www.cnblogs.com/comeony/p/9769767.html

你可能感兴趣的文章
自定义UITableViewController的分组样式
查看>>
[POJ3041 Asteroids]
查看>>
MyEclipse设置编码格式
查看>>
树强化
查看>>
python web 框架(三)-- django & tornado比较
查看>>
[Python]Python中的包(Package)
查看>>
Atom编辑器在windows下怎么更改安装路径
查看>>
vue学习之四组件系统
查看>>
pwa介绍学习
查看>>
go 实现简单的加权分配
查看>>
PhpExcel中文帮助手册|PhpExcel使用方法
查看>>
Linux下安装rpm出现error: Failed dependencies
查看>>
Chapter 6 排序
查看>>
JAVA的StringBuffer类
查看>>
通用的运营商/数字在C#
查看>>
学习Android过程中遇到的未解决问题(个人笔记,细节补充,随时更新)
查看>>
7kyu Jaden Casing Strings
查看>>
主流编程语言的大概方向(个人理解)
查看>>
2015 HUAS Provincial Select Contest #1 A
查看>>
C++string与int的相互转换(使用C++11)
查看>>