《零基礎入門學習Python》第053講:論一隻爬蟲的自我修養
目錄
0. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!
0. 請問 URL 是“統一資源識別符號”還是“統一資源定位符”?
2. 設想一下,如果你是負責開發百度蜘蛛的攻城獅,你在設計爬蟲時應該特別注意什麼問題?
3. 設想一下,如果你是網站的開發者,你應該如何禁止百度爬蟲訪問你網站中的敏感內容?
4. urllib.request.urlopen() 返回的是什麼型別的資料?
6. 魚C工作室(https://ilovefishc.com)的主頁採用什麼編碼傳輸的?
0. 下載魚C工作室首頁(https://ilovefishc.com),並列印前三百個位元組。
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()