【Crawler】Day 27: 爬爬爬,向前爬!網路爬蟲速成班!(上)

在我們過去一起經歷的旅程中,我們從一開始的正規表達式、詞頻、N-Gram,一直到機器學習,像是貝氏分類器、羅吉斯迴歸等等,接著又講到了深度學習,利用神經網路來進行自然語言處理,比如說像是循環神經網路、長短期記憶等等,後來又發展出了自注意機制,有了 Transformer 以及 BERT 還有他的芝麻街小夥伴,又學到了以語言學基礎的工具 Articut 以及 Loki。

我們一起學習了好多好多的語言模型,但不知道你有沒有想過,處理這些語言資料的我們,學了那麼多的模型,那資料從哪來?我們想要學習處理語言資料,但是卻沒有語言資料,那不就像是雞蛋布丁沒有雞蛋、哆啦A夢沒有百寶袋、太陽餅沒有太陽、老婆餅沒有老婆 等等,如果你有看電馭叛客:邊緣行者的話,那就可能有。那其實要取得這些資料,有很大一部分是取自於網路上的資源(但也必須要守法喔!)取得這些資源的方法就是利用網路爬蟲!今天的內容都是爬蟲的先備知識,要熟悉這些技巧才有辦法駕馭爬蟲喔!

小複習

還記得我們在剛開始旅程的時候,曾經有講過串列 list 以及 字典dict嗎?我們在這邊再複習一遍。

串列 list

我們可以透過串列來儲存一系列的資料,程式碼中透過中括號將資料包起來的就是 list ,並透過索引值(index number)來取得串列中的資料。

1
2
3
4
5
myLIST = [1, 2, 3, 4, 5, 6, 7, 8, 9]
myStrLIST = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
print(type(myLIST))
print(myStrLIST[0])
print(type(myStrLIST[0]))
1
2
3
<class 'list'>
1
<class 'str'>

字典 dict

至於字典就是像平常我們查字典一樣,每一個字會有一個對應的解釋,在 Python 中,我們稱呼「字」為鍵(key),「解釋」則為值(value)。
例如:

1
2
3
4
5
strawhat = {
"船長": "魯夫",
"戰鬥員": "索隆",
"航海士": "娜美",
}

另外,我們也可以將字典加入串列中! dict in list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
strawhat = {
"船長": "魯夫",
"戰鬥員": "索隆",
"航海士": "娜美",
}

heart = {
"船長": "托拉法爾加羅",
"副船長": "培波",
"船員一號": "佩金",
"船員二號": "夏奇",
"船員三號": "強帕爾"
}

aliance = []
aliance.append(strawhat)
aliance.append(heart)
print(aliance[1])
print(aliance[1]['船長'])
1
2
{'船長': '托拉法爾加羅', '副船長': '培波', '船員': '佩金', '二號船員': '夏奇', '三號船員': '強帕爾'}
托拉法爾加羅

認識 JSON

由於網站跟網站之間傳遞資料,最常用的資料形式就是 json 檔,而爬蟲所要做的就是要去抓取網頁中的這些 json 檔,或是在網頁上的元素。形式可以是一串 list,或是 dict in list,或是單一一個dict 都可以轉換成 json 檔。 json 檔的要點如下:

  • JavaScript Object Notation 的縮寫
  • 常用於網站上的資料呈現、傳輸
  • 可在 JSON 之內加入相同的基本資料類型

輸出 json 檔

在這裡,我們用先前的串列aliance來輸出 json 檔,並存成檔名為data.json

1
2
with open('data.json', 'w') as f:
json.dump(aliance, f)

讀取 json 檔

在這裡,我們則是將 同一個檔案路徑下的data.json讀取進 Python,並取變數名為 pirates。

1
2
with open('data.json') as f:
pirates = json.load(f)

Try…Except 一個寫程式時眼不見為淨的好幫手(誤)

在寫爬蟲時,很有可能會碰到一個窘境,就是網路資料路徑可能會有改變,或著是你爬得太猖狂,爬到網頁管理者覺得你太超過了,擋你 IP 之類的,這時候你寫的爬蟲程式碼可能就會出現錯誤。那出現錯誤,程式就會立刻停止了耶!這時該怎麼辦呢?讓爬蟲繼續爬嗎?還是你要他進行什麼動作?

在這個時候就會需要進行所謂的例外處理,寫法就是 try...except。其實你可以想像就是你跟電腦說:「你先試試看(這樣那樣) 除非(發生什麼事)你再(幹嘛幹嘛)。」來看看實作怎麼做吧!

假如說你今天寫了一個相加的程式:

1
2
a = input('輸入數字:')
print(a + 1)

運行程式之後絕對會出現錯誤:

1
2
3
4
5
6
7
8
9
輸入數字:1
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-1-f0bc7ffa95ff> in <module>
1 a = input('輸入數字:')
----> 2 print(a + 1)

TypeError: can only concatenate str (not "int") to str

這是因為你輸入的資料型態其實不是「數字」,而是「字串」。「字串」只能跟「字串」相加,不能跟數字相加,所以這時候就出現錯誤。

那這時候我們可以加入 try...except...

1
2
3
4
5
try:                      # 使用 try,測試內容是否正確
a = input('輸入數字:')
print(a + 1)
except:
print('發生錯誤')
1
發生錯誤

這邊就會直接跳過錯誤訊息,直接執行except後面的動作,在這邊就是印出「發生錯誤」。

那你可以仔細觀察一下前面出現錯誤的訊息,你可以發現有一行:

TypeError: can only concatenate str (not “int”) to str

前面的 TypeError 指的就是這個錯誤的類型,有很多種的 Error message,例如:

  • SyntaxError: 程式不合 Python 語法。
  • IndexError: index 位置中沒有東西,或是超出 list 的長度!
  • FileNotFoundError: 找不到檔案
  • ModuleNotFoundError: 可能要檢查一下套件
  • EOFError: 可以換新的 code block 再寫一遍,才可能解決了。

有時候會碰到各種不同的 error。你可以根據不同的 error 去決定你的 try…except 要怎麼寫,最後一個沒有寫的 except 則是一旦發生錯誤,而前面又沒有指定時,就會跑到這個區塊,例如:

1
2
3
4
5
6
7
try:                      # 使用 try,測試內容是否正確
a = input('輸入數字:')
print(a + 1)
except TypeError: # 如果 try 的內容發生錯誤,就執行 except 裡的內容
print('發生錯誤')
except:
print('跑來這裡')
1
2
輸入數字:1
發生錯誤

好的,今天的內容就是這些!明天就會開始正式進入爬蟲啦!