福大軟工1816 · 第五次做業 - 結對做業2

結對同窗的博客地址html

本做業博客的連接python

github地址git

具體分工

  • 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完成,代碼是本身寫的github

思路:正則表達式

​ 爬取CVPR2018網頁內容,用正則表達式將網頁內容中論文連接提早到字符串數組中。算法

​ 遍歷數組,在每次訪問論文網頁的過程當中將論文標題和摘要爬取出來。數組

​ 根據題目要求的輸出格式,將數據輸出到result.txt文件中。網絡

代碼:python爬蟲

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)
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")`

代碼組織與內部實現設計(類圖)

main():處理命令行輸入的字符串,把處理後參數傳遞給其餘函數。
CharNum():統計字符數量。
LineNum():統計行數量。
WordNum():統計單詞數量。
Word_Fre():經過主函數傳遞的-m,-n,-w參數來輸出詞頻前n的詞組。dom

說明算法的關鍵與關鍵實現部分流程圖

因爲本次做業與我的做業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 <PAIR> 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()<n)
        for (int i = 0; i != Word_Num_vec.size(); ++i) {
            const char *ss = Word_Num_vec[i].first.c_str();
            //cout << ss << ":" << Word_Num_vec[i].second << endl;
            fprintf(stream, "<%s>: %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);

附加題設計與展現

附加題及爬蟲python代碼實現和爬蟲數據文本戳這裏

展現發文數量前十的做者:

思路:和前面爬摘要部分同樣,用python能夠將咱們想要的做者名字爬取下來,而後用Count函數返回出現頻率最高的十名做者。

結果展現圖:(右側是做者在頂會上發佈論文的數量)

代碼以下:

from bs4 import BeautifulSoup
from urllib.request import urlopen
import re
import random
filename = '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 Counter
f=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庫能夠很輕易地實現這一功能

效果圖以下:

代碼以下:

import wordcloud
f=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來存儲,更爲直觀

效果圖:

代碼以下:

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

結果爲:

性能報告以下:

顯然耗時最大的函數爲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 經過

運行結果以下:

部分代碼展現以下:

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的代碼簽入記錄

遇到的代碼模塊異常或結對困難及解決方法

問題描述:

剛開始知道要爬數據的時候,第一時間就想到了python,上網看了一些資料,原本打算用scrapy來爬,結果美好的一天在不斷的裝各類庫以及失敗中結束。

作過哪些嘗試:

剛開始確定是堅持,無論怎麼樣必定要把數據爬出來,失敗了幾回以後就將這個任務挪到了國慶後,國慶後靜下心來,從新學習,發現並不困難(主要是放棄了scrapy而用beautifulsoup)

是否解決:

算是圓滿地解決了,成功地爬出了數據,並作了一些附加題小小的嘗試,但很遺憾的是時間有限,沒能把想作的都作出來。雖然deadline是第一輩子產力,但這個生產力並非無限的。

有何收穫:

有,好比軟工實踐仍是要趁早作,就像前面說的,deadline並非萬能的。

還有學習新知識必定要耐心,如何從浩瀚的網絡海洋中學到你想要的,這是一門人生的必修課

評價你的隊友

個人隊友楊喜源,強無敵,擁有着快速的學習能力和清晰的頭腦,老師佈置的做業須要什麼,他就能在短短的時間內學會,並轉換成成果。自我學習是冷靜清晰的頭腦,這是我須要向他學習的。

固然他也有須要改進的地方,好比太淡定了,在deadline的時候依舊能夠談笑風生,這種心態值得學習,可是也形成了咱們進度上的緩慢。

學習進度條

第N周 新增代碼(行) 累計代碼(行) 本週學習耗時(小時) 累計學習耗時(小時) 重要成長
1 300 300 15 15 熟悉了C++語言,瞭解了單元測試,代碼覆蓋率和性能分析
2 0 300 8 23 瞭解了需求分析
3 200 500 8 31 學習了python爬蟲,正則表達式,Wordcloud詞雲,複習了C++代碼
...
相關文章
相關標籤/搜索