close

====================================================================

這篇文章是參加「 第一屆 Python 網路爬蟲實戰研習馬拉松 」活動期末爬蟲程式設計的專題報告,關於這活動的所有細節可以在網路上搜尋到,有興趣的朋友可以參閱活動網站資訊。報告中的完整程式會在所有活動告一段落後再提供連結。

====================================================================

一、 專題摘要

1.  期末專題主題

我選擇的期末專題主題是在cupoy 網站中爬取「科技」分類的新聞。

https://www.cupoy.com/newsfeed/topicgrp/tech_tw

所使用的方法是在「Python 網路爬蟲實戰研習馬拉松」期中所學得的第三方套件和方法,並結合基本Python 語言的特色,這之中還使用pandas.DataFramepandas.Series套件與Python excel檔案和純文字檔存取的功能。

2.  期末專題基本目標

期末專題的基本目標有三個。

其一,透過Google Chrome 開發者工具確認被爬取網站的特性,是靜態網站、還是動態網站?透過開發者工具,發現當我們下滑網頁時,requests 的數量不斷增加而網頁的框架並未改變,因此cupoy 網站是屬於動態網站

其二,根據網站的特性,分別使用 requestsBeautifulSoup 和 selenium 套件來爬取並處理被爬取的網站內容。

其三,將所爬取的內容以 pandas.DataFrame 資料結構儲存,包含:編號(No.)、標題(Title)、文章網址(url)、文章分類(Classification)、文章摘要(Brief)等欄位,並將這些資訊儲存成 Excel 檔案。接著將所爬取的500+ 篇文章的資訊透過文章網址,分別爬取這500+篇文章,並將這500+ 篇文章依編號分別儲存成純文字(txt)檔案。同時,計算這500+ 篇文章所屬分類的篇數,再用 pandas.Series.plot.bar()方法繪製長條圖。

3. 期末專題進階目標

期末專題有四個進階目標。

其一,將爬下的文章,透過 jieba (結巴)中文斷詞工具將爬下的文章切割成「」的大小。

其二,計算同樣文字、詞出現的頻率。將切割好的「」存成 pandas.Series 格式,再用 pandas.Series.value_counts() 方法計算每個「」出現的次數。

其三,過濾經常出現的「Stop Words」之後,再重新統計「」出現的頻率。「Stop Words」指的是經常出現在文章裡各種不同地方,多半是輔助文章中句子的文法結構,不太能反映詞的真正意向,因出現太過頻繁,可當作贅詞來看。在語意分析中,常需要過濾掉「Stop Words」,再來進一步分析和處理。

其四,將過濾後的「」以 world cloud 文字雲的方式呈現,所需要的工具就是 worldcloud  套件。

 

二、實作方法介紹

1.  使用的程式碼介紹

專題中使用 BeautifulSoupseleniummatplotlibjiebaworldcloud 套件,必須事先安裝妥當。

1)  載入所有套件。

image

2)  指定cupoy 網頁位址。

image

3)  selenium 模擬開啟Chrome browser

image

4)  以 selenium 套件模擬開啟 Chrome browser 並連線到 cupoy 網站後開始爬取所需的資料。

i)  首先設定好 news_list 的資料結構,包含它所需要的欄位,我在設計中是以 pandas.DataFrame 資料結構來處理新聞文章清單,而它的欄位有:

No.: 這是自訂的編號,以字串而非數字型態表示,長度為3。這個編號也用來作為儲存文章文字檔的檔案名稱。

Title: 用來儲存cupoy 裡文章的標題。

url: 用來儲存cupoy 裡文章的外部網頁連結。

Classification: 用來儲存cupoy 裡文章的分類。

brief: 用來儲存 cupoy 裡文章的摘要。

ii)  因為這個網站是動態網站,因此當網頁下滑時,Chrome browser 會不停的更新網頁內容,但不幸的是,這個網站的設計是在網頁下滑時滑過的網頁內容會被拋棄,不會繼續存在網頁裡,因此在 while loop 裡有兩件主要的工作:

a)  讀取和分析網頁內容。

b) 下滑網頁並等待更新內容。

iii) 在讀取內容時,有一件事必須特別注意,就是每爬下一筆新聞就要判斷該筆新聞是否已經被爬取過並儲存在 news_list ,原因是當網頁下滑時,仍有少部分新聞有可能還在這網頁裡,因此必須過濾掉已儲存的新聞。判斷的方式是利用新聞的 Title 欄位,判斷條件是:

if news_title not in list(news_list['Title']):

iv) 另外,一個要注意的是判斷網頁是否已經下滑到網頁最底部。這裡用了兩個變數: new_height last_height ,分別記錄這一次下滑和前一次下滑的位置,網頁位置的取得是透過 Python 呼叫 javascript 所取得。

browser.execute_script("return document.body.scrollHeight")

v) 最後,下滑網頁後到網頁更新完畢之間的等待時間是透過反覆測試後決定的,它和網路狀態、網站頻寬、存取當時的網站擁擠程度有關,為確保程式不因連線時間截止而出現錯誤,通常等待時間會設得略長一些。

vi) 還有,在程式裡我並沒有讓被爬取文章數目一到500篇就停下來,而是繼續爬完這次下滑網頁裡所有文章,所以文章篇數會略多於500篇。

image

5)  結束爬取 cupoy 網站後,關閉 Chrome browser

image

6)  將爬下的500+篇文章清單儲存到名為 news_list.xlsx Excel 檔案裡。請注意這裡有一個 warning 出現,原因是有一篇文章的外部網頁地址(url)長度超過Excel 儲存格限制的255個字元,因此這篇文章的外部網頁地址並沒被儲存到 Excel 檔案裡。

image

7)  載入 os 套件,目的是要建立儲存 500+ 篇文章的路徑。

image

8)  首先先檢查文章要儲存的目錄(articles)是否存在,倘若不存在就建立一個新目錄。

接著在 for loop 裡依序將 news_list 裡的每一則文章清單抓出,欄位 No. 加上 .txt當作檔案名稱,欄位 url 裡的外部地址則透過 requests BeautifulSoup 套件爬下,這和爬取 cupoy 網站的方式相同。要注意的是新聞文章在各網站網頁中 html 的儲存格式是將純文字資訊包在 <p>…</p> 之中,所以只要截取 <p>…</p> 裡的文字就掌握這篇文章大部分的資訊。

要注意的是,在儲存純文字到檔案裡時要對每一段落自行加上換行符號 \n

image

9)  在用 matplotlib 套件畫統計圖時遇到無法顯示中文的問題,在網路上搜尋相關中文顯示的文章,但都沒有效用,最後一篇文章提供的方法確實解決了中文顯示的問題。

i)  先將 windows 字型庫裡你想用來顯示中文的字型檔案(例如:mingliu 細明體)複製一份到 matplotlib 目錄底下名為 ttf的目錄下。我的路徑是:

C:\ProgramData\Anaconda3\pkgs\matplotlib-3.1.0-py37hc8f65d3_0\Lib\site-packages\matplotlib\mpl-data\fonts\ttf

ii) 接著在程式裡加上這兩行指令將預設字型設定為中文字型。

matplotlib.rcParams['font.sans-serif'] = ['mingliu']

matplotlib.rcParams['font.family'] = 'sans-serif'

所有 matplotlib 中文顯示的細節請參考下列網頁。

https://blog.csdn.net/dgatiger/article/details/50414549

image

10)  接著將所有新聞清單透過 pandas.DataFrame.groupby() 方法予以分類,分類後再以 pandas.core.groupby.DataFrameGroupBy.count() 方法計算每個分類的數量,最後記得將這分類數量轉換成 pandas.Series 資料結構。

畫統計圖就用 pandas.Series.plot.bar() 方法,這方法會去呼叫 matplotlib 套件的相關方法。

image

11)  接下來是進階處理的部分,先載入中文斷詞套件 jieba

image

12)  由於程式前面已經將所有500+篇文章全數下載並儲存成純文字檔案,所以這邊只要直接從指定目錄讀取文字檔即可,而這 jieba 斷詞的演示和後續的操作就以第一篇文章為例。

關於斷詞的部分可以直接呼叫 jieba.cut() 方法,這方法會返回切割好的生成器物件(generator object,再以 for loop 方式取出儲存成字串串列(list of string)。

詳細結巴 jieba 操作請參閱以下網站。

https://github.com/fxsjy/jieba

https://github.com/ldkrsi/jieba-zh_TW

https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/32296/

image

13) 要統計所有斷詞分別出現多少次,可以先將之前的字串串列轉換成 pandas.Series 資料結構,再利用 pandas.Series.value_counts() 方法計算每一個詞出現的次數。

image

14)  Stop Words」指的是句子中不重要但卻頻繁出現的字詞,刪除這些字詞並不會影響句子所要表達的意向,在過濾掉這些「Stop Words」之後卻有助於句子或文章的分析,通常這些「Stop Words」是因不同需求自行定義的。

i)  首先,讀取是先定義好的「Stop Words」並儲存到stopWords 串列之中。

ii)  接著,從資料夾中讀取要被斷詞的文章,並呼叫 jieba.cut() 方法斷詞,並暫存在 segments 變數中。

iii)  然後透過 Python 特殊的 filter() 方法,它是一個產生器運算式,一一過濾 segments 中出現在 stopWords清單中的字詞。

iv) 最後將字串串列remainderWords轉換成 pandas.Series 資料結構,再利用 pandas.Series.value_counts() 方法計算每一個詞出現的次數。

關於「Stop Words」的說明,可以參考以下連結。

https://github.com/tomlinNTUB/Python-in-5-days/blob/master/10-2%20%E4%B8%AD%E6%96%87%E6%96%B7%E8%A9%9E-%E7%A7%BB%E9%99%A4%E5%81%9C%E7%94%A8%E8%A9%9E.md

image

15)  文字雲(word cloud)的呈現是透過 wordcloud 套件來完成。

i)  重要的是中文字型的顯示必須指定字型路徑才能正確顯示。

font_path = 'C:\Windows\Fonts/KAIU.TTF' # 標楷體

ii)  在產生文字雲之前,先要將先前的字串串列轉換成以一個空白(whitespace)隔開的字串,然後呼叫 WorldCloud.generdtor() 方法轉換成 matplotlib.pyplot 物件。

iii) 最後,透過 matplotlib 套件中 matplotlib.pyplot.imshow() 方法將文字雲顯現出來。

image

2.  使用的模組介紹

這個專題程式沒有複雜到要寫個模組來處理,這部分就省略了。

 

三、 成果展示 ​​​​​​​

第四段程式執行完畢後,打印出來的資料包含:文章編號、文章標題、文章外部網路連結、文章分類、文章摘要。

image

第六段程式執行後,產生下面這樣的 Excel 檔案。

image

第八段程式執行後,除了打印如下的信息之外,還建立 articles 目錄,裡頭有500+ 篇純文字檔。

image

image

第十段程式執行後打印下面這樣的信息,並同時畫出 bar chart

image

image

第十二段程式執行後產生的斷詞串列如下。

image

第十三段程式執行後打印出斷詞的出現統計資訊。

image

在執行第十四段程式後打印出過濾 Stop Words 後斷詞的統計數目。

image

最後,第十五斷程式執行後所產生的文字雲如下。

image

 

四、 結論 ​​​​​​​

這次的專題並沒有使用到太複雜的套件或者要突破訪火牆、反爬蟲之類的障礙,而這個練習在於訓練 coder 的耐心,從爬取 cupoy 網頁開始,可以對照 Chrome browser 開發者工具和爬下文網頁打印訊息,判斷是否有抓到想要的資訊。

此外,下滑 cupoy 網頁時,先前網頁內容被拋棄是一種網頁設計的方式,如果是先下滑網頁到最底部,再去爬取網頁資料的話,會發現永遠只爬取到少部分網頁資料。

爬取 cupoy 網頁時還發現,明明爬得是「科技類」網頁,但當網頁不停下滑時,卻會下滑到其他類別的網頁,而造成似乎永遠爬不完這個網站似的。

所以,再爬取 cupoy 這個網站時,要邊下滑網頁邊讀取資料,同時判斷讀了多少筆文章、文章是否重複。

網路爬蟲,除了透過工具爬取資料之外,最重要的部分是在處理資料、整理資料成想要的資料結構和格式,再分析這些資料、找出有用的部分,再來將資訊以可閱讀(readable)的型態呈現出來。

 

 

老驥於 2020/02/21

 

arrow
arrow
    文章標籤
    Python 網路爬蟲 學習
    全站熱搜
    創作者介紹
    創作者 phd.chi 的頭像
    phd.chi

    maximaChi's blog

    phd.chi 發表在 痞客邦 留言(0) 人氣()