1. 程式人生 > >《零基礎入門學習Python》第053講:論一隻爬蟲的自我修養

《零基礎入門學習Python》第053講:論一隻爬蟲的自我修養

目錄

0. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!

測試題

0. 請問 URL 是“統一資源識別符號”還是“統一資源定位符”?

1. 什麼是爬蟲?

2. 設想一下,如果你是負責開發百度蜘蛛的攻城獅,你在設計爬蟲時應該特別注意什麼問題?

3. 設想一下,如果你是網站的開發者,你應該如何禁止百度爬蟲訪問你網站中的敏感內容?

4. urllib.request.urlopen() 返回的是什麼型別的資料?

5. 如果訪問的網址不存在,會產生哪類異常?

6. 魚C工作室(https://ilovefishc.com)的主頁採用什麼編碼傳輸的?

7. 為了解決 ASCII 編碼的不足,什麼編碼應運而生?

動動手

0. 下載魚C工作室首頁(https://ilovefishc.com),並列印前三百個位元組。

1. 寫一個程式,檢測指定 URL 的編碼。

2. 寫一個程式,依次訪問檔案中指定的站點,並將每個站點返回的內容依次存放到不同的檔案中。


0. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!

馬上我們的教學就要進入最後一個章節,Pygame 嗨爆引爆全場,但由於發生了一個小插曲,所以這裡決定追加一個章節,因為有人反應說:“你上一節課教我們去查詢文件,教我們如何從官方文件中找到需要的答案,但是我發現知易行難也,希望舉一個詳細點的例子,教我們如何去查詢。”

所以這裡我們詳細的深刻的講一下網路爬蟲。所以就有了本章節,論一隻爬蟲的自我修養。

首先,我們需要理解,什麼是網路爬蟲,如圖:

網路爬蟲又稱為網路蜘蛛(Spider),如果你把整個網際網路想象為一個蜘蛛網的構造,每個網站或域名都是一個節點,那我們這隻蜘蛛就是在上面爬來爬去,在不同的網頁上爬來爬去,順便獲得我們需要的資源,抓取最有用的。做過網站的朋友一定很熟悉,我們之所以能夠通過百度、谷歌這樣的搜尋引擎檢索到你的網頁,靠的就是他們每天派出大量的蜘蛛在網際網路上爬來爬去,對網頁中的每個關鍵字建立索引,然後建立索引資料庫,經過了複雜的排序演算法之後,這些結果將按照搜尋關鍵詞的相關度的高低展現在我們的眼前。那當然,現在讓你編寫一個搜尋引擎是一件非常苦難的、不可能完成的事情,但是有一句老話說的好啊:千里之行,始於足下。我們先從編寫一段小爬蟲程式碼開始,然後不斷地來改進它,要使用Python編寫爬蟲程式碼,我們要解決的第一個問題是:

Python如何訪問網際網路?

好在Python為此準備好了電池,Python為此準備的電池叫做:urllib

urllib 事實上是由兩個單片語成的:URL(就是我們平時說的網頁地址) 和 lib(就是library的意思,就是首頁)。

•URL的一般格式為(帶方括號[]的為可選項):

protocol :// hostname[:port] / path / [;parameters][?query]#fragment

•URL由三部分組成:

–第一部分是協議(protocol):http,https,ftp,file,ed2k…

–第二部分是存放資源的伺服器的域名系統或IP地址(有時候要包含埠號,各種傳輸協議都有預設的埠號,如http的預設埠為80)。

–第三部分是資源的具體地址,如目錄或檔名等。

那好,說完URL,我們現在可以來談一下 urllib 這個模組了,Python 3 其實對這個模組進行了挺大的改動,以前有urllib和urllib2 兩個模組,Python3 乾脆把它們合併在了一起並做了統一。其實 urllib 並不是一個模組,而是一個包。我們來查一下文件就知道了(我們說過,有問題,找文件。)

urllib 其實是一個包,其中包含4個模組,request 、error、parse 和 robotparser,我們主要會來講解 request 這個模組,這個模組也是最複雜的,因為它包含了對伺服器的請求和發出、跳轉、代理、安全等幾大方面。

我們點進去會發現文件非常長,從頭看到尾是不可能的,這時候怎麼辦呢?建議百度、谷歌,查詢 urllib.request 的用法,或者查詢 Python3 如何訪問網頁,也可以得到想要的結果。你會得到,使用 urlopen() 這個函式。

urlopen() 函式除了第一個引數 url 是必需的外,後面的都有預設引數和可選引數,文件告訴我們,url 可以是一個字串或者 Request object。Request object 是什麼,我們下節課講解。我們猜測 url 為一個字串是應該就是域名地址的字串,我們先來嚐個鮮:(之所以選擇https://ilovefishc.com這個網頁,是因為這個網頁的原始碼量較少,其他的網站會直接把IELD搞崩潰,不信你可以試試 https://www.baidu.com。)

>>> import urllib.request
>>> response = urllib.request.urlopen("https://ilovefishc.com")
>>> html = response.read()
>>> print(html)
b'<!DOCTYPE html>\n<html lang="en">\n<head>\n    <meta charset="UTF-8">\n    <meta name="viewport" content="width=device-width, initial-scale=1.0">\n    <meta name="keywords" content="\xe9\xb1\xbcC\xe5\xb7\xa5\xe4\xbd\x9c\xe5\xae\xa4|\xe5\x85\x8d\xe8\xb4\xb9\xe7\xbc\x96\xe7\xa8\x8b\xe8\xa7\x86\xe9\xa2\x91\xe6\x95\x99\xe5\xad\xa6|Python\xe6\x95\x99\xe5\xad\xa6|Web\xe5\xbc\x80\xe5\x8f\x91\xe6\x95\x99\xe5\xad\xa6|\xe5\x85\xa8\xe6\xa0\x88\xe5\xbc\x80\xe5\x8f\x91\xe6\x95\x99\xe5\xad\xa6|C\xe8\xaf\xad\xe8\xa8\x80\xe6\x95\x99\xe5\xad\xa6|\xe6\xb1\x87\xe7\xbc\x96\xe6\x95\x99\xe5\xad\xa6|Win32\xe5\xbc\x80\xe5\x8f\x91|\xe5\x8a\xa0\xe5\xaf\x86\xe4\xb8\x8e\xe8\xa7\xa3\xe5\xaf\x86|Linux\xe6\x95\x99\xe5\xad\xa6">\n    <meta name="description" content="\xe9\xb1\xbcC\xe5\xb7\xa5\xe4\xbd\x9c\xe5\xae\xa4\xe4\xb8\xba\xe5\xa4\xa7\xe5\xae\xb6\xe6\x8f\x90\xe4\xbe\x9b\xe6\x9c\x80\xe6\x9c\x89\xe8\xb6\xa3\xe7\x9a\x84\xe7\xbc\x96\xe7\xa8\x8b\xe8\xa7\x86\xe9\xa2\x91\xe6\x95\x99\xe5\xad\xa6\xe3\x80\x82">\n    <meta name="author" content="\xe9\xb1\xbcC\xe5\xb7\xa5\xe4\xbd\x9c\xe5\xae\xa4">\n    <title>\xe9\xb1\xbcC\xe5\xb7\xa5\xe4\xbd\x9c\xe5\xae\xa4-\xe5\x85\x8d\xe8\xb4\xb9\xe7\xbc\x96\xe7\xa8\x8b\xe8\xa7\x86\xe9\xa2\x91\xe6\x95\x99\xe5\xad\xa6|Python\xe6\x95\x99\xe5\xad\xa6|Web\xe5\xbc\x80\xe5\x8f\x91\xe6\x95\x99\xe5\xad\xa6|\xe5\x85\xa8\xe6\xa0\x88\xe5\xbc\x80\xe5\x8f\x91\xe6\x95\x99\xe5\xad\xa6|C\xe8\xaf\xad\xe8\xa8\x80\xe6\x95\x99\xe5\xad\xa6|\xe6\xb1\x87\xe7\xbc\x96\xe6\x95\x99\xe5\xad\xa6|Win32\xe5\xbc\x80\xe5\x8f\x91|\xe5\x8a\xa0\xe5\xaf\x86\xe4\xb8\x8e\xe8\xa7\xa3\xe5\xaf\x86|Linux\xe6\x95\x99\xe5\xad\xa6</title>\n    <link rel="shortcut icon" type="image/x-icon" href="img/favicon.ico">\n    <link rel="stylesheet" href="css/styles.css">\n    <script src="js/jq.js"></script>\n    <script src="https://cdn.bootcss.com/timelinejs/2.36.0/js/storyjs-embed.js"></script>\n    <script src="https://cdnjs.cloudflare.com/ajax/libs/timelinejs/2.36.0/js/storyjs-embed.js" defer></script>\n    <!-- <script src="https://fishc.oss-cn-hangzhou.aliyuncs.com/Web/js/embed.js"></script> -->\n    <script>\n        $(document).ready(function() {\n            var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;\n\n            createStoryJS({\n                type:       \'timeline\',\n                width:      \'auto\',\n                height:     windowHeight,\n                source:     \'data.json\',\n                start_at_end:true,                          //OPTIONAL START AT LATEST DATE\n                embed_id:   \'my-timeline\'\n            });\n\n        });\n    </script>\n    <!-- END TimelineJS -->\n</head>\n<body>\n<div id="my-timeline"></div>\n</body>\n</html>\n'

得到的字串是一個以 b 開頭(二進位制)字串。得到的字串似乎和我們所理解的網頁程式碼不一樣,但是我們又看到了很熟悉的身影(例如:div,link rel等),但是在這個網頁,如果右鍵審查元素

我們會發現網頁的程式碼很整齊,那這是怎麼回事呢?為什麼Python這裡高的一團糟,我們剛才說了,Python這裡直接得到的是byte型別(二進位制編碼),所以我們可以對它進行解碼操作,我們先來看一下這個網頁的編碼方式是:UTF-8。

>>> html = html.decode("utf-8")
>>> print(html)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="keywords" content="魚C工作室|免費程式設計視訊教學|Python教學|Web開發教學|全棧開發教學|C語言教學|彙編教學|Win32開發|加密與解密|Linux教學">
    <meta name="description" content="魚C工作室為大家提供最有趣的程式設計視訊教學。">
    <meta name="author" content="魚C工作室">
    <title>魚C工作室-免費程式設計視訊教學|Python教學|Web開發教學|全棧開發教學|C語言教學|彙編教學|Win32開發|加密與解密|Linux教學</title>
    <link rel="shortcut icon" type="image/x-icon" href="img/favicon.ico">
    <link rel="stylesheet" href="css/styles.css">
    <script src="js/jq.js"></script>
    <script src="https://cdn.bootcss.com/timelinejs/2.36.0/js/storyjs-embed.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/timelinejs/2.36.0/js/storyjs-embed.js" defer></script>
    <!-- <script src="https://fishc.oss-cn-hangzhou.aliyuncs.com/Web/js/embed.js"></script> -->
    <script>
        $(document).ready(function() {
            var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;

            createStoryJS({
                type:       'timeline',
                width:      'auto',
                height:     windowHeight,
                source:     'data.json',
                start_at_end:true,                          //OPTIONAL START AT LATEST DATE
                embed_id:   'my-timeline'
            });

        });
    </script>
    <!-- END TimelineJS -->
</head>
<body>
<div id="my-timeline"></div>
</body>
</html>

測試題

0. 請問 URL 是“統一資源識別符號”還是“統一資源定位符”?

答:往後的學習你可能會經常接觸 URL 和 URI,為了防止你突然懵倒,所以在這裡給大家簡單普及下。URI 是統一資源識別符號(Universal Resource Identifier),URL 是統一資源定位符(Universal Resource Locator)。用一句話概括它們的區別:URI 是用字串來標識某一網際網路資源,而 URL 則是表示資源的地址(我們說某個網站的網址就是 URL),因此 URI 屬於父類,而 URL 屬於 URI 的子類。

1. 什麼是爬蟲?

答:爬蟲事實上就是一個程式,用於沿著網際網路結點爬行,不斷訪問不同的網站,以便獲取它所需要的資源。

2. 設想一下,如果你是負責開發百度蜘蛛的攻城獅,你在設計爬蟲時應該特別注意什麼問題?

答:不要重複爬取同一個 URL 的內容。假設你沒做這方面的預防,如果一個 URL 的內容中包含該 URL 本身,那麼就會陷入無限遞迴。

3. 設想一下,如果你是網站的開發者,你應該如何禁止百度爬蟲訪問你網站中的敏感內容?

(課堂上沒講,可以自行百度答案)

答:在網站的根目錄下建立並編輯 robots.txt 檔案,用於表明您不希望搜尋引擎抓取工具訪問您網站上的哪些內容。此檔案使用的是 Robots 排除標準,該標準是一項協議,所有正規搜尋引擎的蜘蛛均會遵循該協議爬取。既然是協議,那就是需要大家自覺尊重,所以該協議一般對非法爬蟲無效。

4. urllib.request.urlopen() 返回的是什麼型別的資料?

答:返回的是一個HTTPResponse的例項物件,它屬於http.client模組。

>>> response = urllib.request.urlopen("http://www.fishc.com")
>>> type(response)
<class 'http.client.HTTPResponse'>

呼叫其read()方法才能讀出URL的內容。

5. 如果訪問的網址不存在,會產生哪類異常?

(雖然課堂沒講過,但你可以動手試試)

答:HTTPError

6. 魚C工作室(https://ilovefishc.com)的主頁採用什麼編碼傳輸的?

答:UTF-8 編碼。

一般網頁通過點選審查元素,在 <head> 標籤中的 charset 會顯示採用了哪種編碼。

7. 為了解決 ASCII 編碼的不足,什麼編碼應運而生?

答:Unicode 編碼。擴充套件閱讀關於編碼的那篇文章太長了,有魚油說太生澀難懂,對於對編碼問題還一頭霧水的魚油請看 -> 什麼是編碼?


動動手

0. 下載魚C工作室首頁(https://ilovefishc.com),並列印前三百個位元組。

程式碼清單:

>>> import urllib.request
>>> response = urllib.request.urlopen('http://www.fishc.com')
>>> print(response.read(300))
b'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\r\n\t"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r\n\r\n<!-- \r\n(c) 2011 \xc4\xbdubom\xc3\xadr Krupa, CC BY-ND 3.0\r\n -->\t\r\n\r\n<html xmlns="http://www.w3.org/1999/xhtml">\r\n\t<head>\r\n\t\t<meta http-equiv="content-type" content="text/html; charset=utf-8" />\r\n\t\t'

1. 寫一個程式,檢測指定 URL 的編碼。

演示:

提示:

提供個“電池”給你用 -> 一次性解決你所有的編碼檢測問題

程式碼清單:

import urllib.request
import chardet

def main():
    url = input("請輸入URL:")

    response = urllib.request.urlopen(url)
    html = response.read()

    # 識別網頁編碼
    encode = chardet.detect(html)['encoding']
    if encode == 'GB2312':
        encode = 'GBK'

    print("該網頁使用的編碼是:%s" % encode)
        
if __name__ == "__main__":
    main()

2. 寫一個程式,依次訪問檔案中指定的站點,並將每個站點返回的內容依次存放到不同的檔案中。

演示:

urls.txt 檔案存放需要訪問的 ULR:

執行你寫的程式(test.py),依次訪問指定的 URL 並將其內容存放為一個新的檔案:

程式碼清單:

import urllib.request
import chardet

def main():
    i = 0
    
    with open("urls.txt", "r") as f:
        # 讀取待訪問的網址
        # 由於urls.txt每一行一個URL
        # 所以按換行符'\n'分割
        urls = f.read().splitlines()
        
    for each_url in urls:
        response = urllib.request.urlopen(each_url)
        html = response.read()

        # 識別網頁編碼
        encode = chardet.detect(html)['encoding']
        if encode == 'GB2312':
            encode = 'GBK'
        
        i += 1
        filename = "url_%d.txt" % i

        with open(filename, "w", encoding=encode) as each_file:
            each_file.write(html.decode(encode, "ignore"))

if __name__ == "__main__":
    main()