點擊上方「 CSDN 」,選擇「置頂公眾號」
關鍵時刻,第一時間送達!
作者:Jack-Cui,熱愛技術分享,活躍於 CSDN 和知乎,開設的《Python3網絡爬蟲入門》、《Python3機器學習》等專欄受到好評。
聲明:本文講解的實戰內容,均僅用於學習交流,請勿用於任何商業用途!
一、前言
強烈建議:請在電腦的陪同下,閱讀本文。本文以實戰為主,閱讀過程如稍有不適,還望多加練習。
本文的實戰內容有:
網絡小說下載(靜態網站)
優美壁紙下載(動態網站)
愛奇藝VIP視頻下載
二、網絡爬蟲簡介
網絡爬蟲,也叫網絡蜘蛛(Web Spider)。它根據網頁地址(URL)爬取網頁內容,而網頁地址(URL)就是我們在瀏覽器中輸入的網站連結。比如:https://www.baidu.com/ ,它就是一個URL。
在講解爬蟲內容之前,我們需要先學習一項寫爬蟲的必備技能:審查元素(如果已掌握,可跳過此部分內容)。
1. 審查元素
在瀏覽器的地址欄輸入URL地址,在網頁處右鍵單擊,找到檢查,如下圖所示:(不同瀏覽器的叫法不同,Chrome瀏覽器叫做檢查,Firefox瀏覽器叫做查看元素,但是功能都是相同的)
我們可以看到,右側出現了一大推代碼,這些代碼就叫做HTML。什麼是HTML?舉個容易理解的例子:我們的基因決定了我們的原始容貌,伺服器返回的HTML決定了網站的原始容貌。
為啥說是原始容貌呢?因為人可以整容啊!扎心了,有木有?那網站也可以"整容"嗎?可以!請看下圖:
我能有這麼多錢嗎?顯然不可能。我是怎麼給網站"整容"的呢?就是通過修改伺服器返回的HTML信息。我們每個人都是"整容大師",可以修改頁面信息。我們在頁面的哪個位置點擊審查元素,瀏覽器就會為我們定位到相應的HTML位置,進而就可以在本地更改HTML信息。
再舉個小例子:我們都知道,使用瀏覽器"記住密碼"的功能,密碼會變成一堆小黑點,是不可見的。可以讓密碼顯示出來嗎?可以,只需給頁面"動個小手術"!以淘寶為例,在輸入密碼框處右鍵,點擊檢查。
可以看到,瀏覽器為我們自動定位到了相應的HTML位置。將下圖中的password屬性值改為text屬性值(直接在右側代碼處修改):
就這樣,瀏覽器"記住的密碼"顯現出來了:
說這麼多,什麼意思呢?瀏覽器就是作為客戶端從伺服器端獲取信息,然後將信息解析,並展示給我們的。我們可以在本地修改HTML信息,為網頁"整容",但是我們修改的信息不會回傳到伺服器,伺服器存儲的HTML信息不會改變。刷新一下界面,頁面還會回到原本的樣子。這就跟人整容一樣,我們能改變一些表面的東西,但是不能改變我們的基因。
2. 簡單實例
網絡爬蟲的第一步就是根據URL,獲取網頁的HTML信息。在Python3中,可以使用urllib.request和requests進行網頁爬取。
requests庫強大好用,所以本文使用requests庫獲取網頁的HTML信息。requests庫的github地址:https://github.com/requests/requests
(1)requests安裝
在學習使用requests庫之前,我們需要在電腦中安裝好requests庫。在cmd中,使用如下指令安裝requests庫:
pip install requests
easy_install requests
使用pip和easy_install都可以安裝,二選一即可。
(2)簡單實例
安裝好requests庫之後,我們先來大體瀏覽一下requests庫的基礎方法:
官方中文教程地址:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html
requests庫的開發者為我們提供了詳細的中文教程,查詢起來很方便。本文不會對其所有內容進行講解,摘取其部分使用到的內容,進行實戰說明。
首先,讓我們看下requests.get()方法,它用於向伺服器發起GET請求,不了解GET請求沒有關係。我們可以這樣理解:get的中文意思是得到、抓住,那這個requests.get()方法就是從伺服器得到、抓住數據,也就是獲取數據。讓我們看一個例子(以 www.gitbook.cn 為例)來加深理解:
# -*- coding:UTF-8 -*- import requests if __name__ == '__main__': target = 'http://gitbook.cn/' req = requests.get(url=target) print(req.text)
requests.get()方法必須設置的一個參數就是url,因為我們得告訴GET請求,我們的目標是誰,我們要獲取誰的信息。我們將GET請求獲得的響應內容存放到req變量中,然後使用req.text就可以獲得HTML信息了。運行結果如下:
左側是我們程序獲得的結果,右側是我們在www.gitbook.cn 網站審查元素獲得的信息。我們可以看到,我們已經順利獲得了該網頁的HTML信息。這就是一個最簡單的爬蟲實例,可能你會問,我只是爬取了這個網頁的HTML信息,有什麼用呢?客官稍安勿躁,接下來進入我們的實戰正文。
三、爬蟲實戰
實戰內容由簡單到複雜,難度逐漸增加,但均屬於入門級難度。下面開始我們的第一個實戰內容:網絡小說下載。
1. 小說下載
(1)實戰背景
小說網站《筆趣看》URL:http://www.biqukan.com/
《筆趣看》是一個盜版小說網站,這裡有很多起點中文網的小說,該網站小說的更新速度稍滯後於起點中文網正版小說的更新速度。並且該網站只支持在線瀏覽,不支持小說打包下載。因此,本次實戰就是從該網站爬取並保存一本名為《一念永恆》的小說,該小說是耳根正在連載中的一部玄幻小說。PS:本實例僅為交流學習,支持耳根大大,請上起點中文網訂閱。
(2)小試牛刀
我們先看下《一念永恆》小說的第一章內容,URL:http://www.biqukan.com/1_1094/5403177.html
用已經學到的知識獲取HTML信息試一試,編寫代碼如下:
# -*- coding:UTF-8 -*- import requests if __name__ == '__main__': target = 'http://www.biqukan.com/1_1094/5403177.html' req = requests.get(url=target) print(req.text)
運行代碼,可以看到如下結果:
可以看到,我們很輕鬆地獲取了HTML信息。但是,很顯然,很多信息是我們不想看到的,我們只想獲得如右側所示的正文內容,我們不關心那些看著眼暈的英文字母。如何把正文內容從這些眾多的HTML信息中提取出來呢?這就是本小節實戰的主要內容。
(3)Beautiful Soup
爬蟲的第一步,獲取整個網頁的HTML信息,我們已經完成。接下來就是爬蟲的第二步,解析HTML信息,提取我們感興趣的內容。對於本小節的實戰,我們感興趣的內容就是文章的正文。提取的方法有很多,例如使用正則表達式、Xpath、Beautiful Soup等。對於初學者而言,最容易理解,並且使用簡單的方法就是使用Beautiful Soup提取感興趣內容。
Beautiful Soup的安裝方法和requests一樣,使用如下指令安裝(也是二選一):
一個強大的第三方庫,都會有一個詳細的官方文檔。我們很幸運,Beautiful Soup也是有中文的官方文檔。URL:http://beautifulsoup.readthedocs.io/zh_CN/latest/
同理,我會根據實戰需求,講解Beautiful Soup庫的部分使用方法,更詳細的內容,請查看官方文檔。
現在,我們使用已經掌握的審查元素方法,查看一下我們的目標頁面,你會看到如下內容:
不難發現,文章的所有內容都放在了一個名為div的「東西下面」,這個"東西"就是html標籤。HTML標籤是HTML語言中最基本的單位,HTML標籤是HTML最重要的組成部分。不理解,沒關係,我們再舉個簡單的例子:一個女人的包包裡,會有很多東西,她們會根據自己的習慣將自己的東西進行分類。鏡子和口紅這些會經常用到的東西,回歸放到容易拿到的外側口袋裡。那些不經常用到,需要注意安全存放的證件會被放到不容易拿到的裡側口袋裡。
html標籤就像一個個「口袋」,每個「口袋」都有自己的特定功能,負責存放不同的內容。顯然,上述例子中的div標籤下存放了我們關心的正文內容。這個div標籤是這樣的:
細心的朋友可能已經發現,除了div字樣外,還有id和class。id和class就是div標籤的屬性,content和showtxt是屬性值,一個屬性對應一個屬性值。這東西有什麼用?它是用來區分不同的div標籤的,因為div標籤可以有很多,我們怎麼加以區分不同的div標籤呢?就是通過不同的屬性值。
仔細觀察目標網站一番,我們會發現這樣一個事實:class屬性為showtxt的div標籤,獨一份!這個標籤裡面存放的內容,是我們關心的正文部分。
知道這個信息,我們就可以使用Beautiful Soup提取我們想要的內容了,編寫代碼如下:
# -*- coding:UTF-8 -*- from bs4 import BeautifulSoup import requests if __name__ == "__main__": target = 'http://www.biqukan.com/1_1094/5403177.html' req = requests.get(url = target) html = req.text bf = BeautifulSoup(html) texts = bf.find_all('div', class_ = 'showtxt') print(texts)
在解析html之前,我們需要創建一個Beautiful Soup對象。BeautifulSoup函數裡的參數就是我們已經獲得的html信息。然後我們使用find_all方法,獲得html信息中所有class屬性為showtxt的div標籤。find_all方法的第一個參數是獲取的標籤名,第二個參數class_是標籤的屬性,為什麼不是class,而帶了一個下劃線呢?因為python中class是關鍵字,為了防止衝突,這裡使用class_表示標籤的class屬性,class_後面跟著的showtxt就是屬性值了。看下我們要匹配的標籤格式:
這樣對應的看一下,是不是就懂了?可能有人會問了,為什麼不是find_all('div', id = 'content', class_ = 'showtxt')?這樣其實也是可以的,屬性是作為查詢時候的約束條件,添加一個class_='showtxt'條件,我們就已經能夠準確匹配到我們想要的標籤了,所以我們就不必再添加id這個屬性了。運行代碼查看我們匹配的結果:
我們可以看到,我們已經順利匹配到我們關心的正文內容,但是還有一些我們不想要的東西。比如div標籤名,br標籤,以及各種空格。怎麼去除這些東西呢?我們繼續編寫代碼:
# -*- coding:UTF-8 -*- from bs4 import BeautifulSoup import requests if __name__ == "__main__": target = 'http://www.biqukan.com/1_1094/5403177.html' req = requests.get(url = target) html = req.text bf = BeautifulSoup(html) texts = bf.find_all('div', class_ = 'showtxt') print(texts[0].text.replace('\xa0'*8,'\n\n'))
find_all匹配的返回的結果是一個列表。提取匹配結果後,使用text屬性,提取文本內容,濾除br標籤。隨後使用replace方法,剔除空格,替換為回車進行分段。 在html中是用來表示空格的。replace('\xa0'*8,'\n\n')就是去掉下圖的八個空格符號,並用回車代替:
程序運行結果如下:
可以看到,我們很自然的匹配到了所有正文內容,並進行了分段。我們已經順利獲得了一個章節的內容,要想下載正本小說,我們就要獲取每個章節的連結。我們先分析下小說目錄,URL:http://www.biqukan.com/1_1094/
通過審查元素,我們發現可以發現,這些章節都存放在了class屬性為listmain的div標籤下,選取部分html代碼如下:
在分析之前,讓我們先介紹一個概念:父節點、子節點、孫節點。 和 標籤的開始和結束的位置,他們是成對出現的,有開始位置,就有結束位置。我們可以看到,在 標籤包含 標籤的子節點, 標籤的孫節點。有點繞?那你記住這句話:誰包含誰,誰就是誰兒子!標籤,那這個
標籤就是
標籤又包含
他們之間的關係都是相對的。比如對於標籤。這跟我們人是一樣的,上有老下有小。
看到這裡可能有人會問,這有好多
好了,概念明確清楚,接下來,讓我們分析一下問題。我們看到每個章節的名字存放在了標籤裡面。標籤還有一個href屬性。這裡就不得不提一下標籤的定義了, 標籤定義了一個超連結,用於從一張頁面連結到另一張頁面。 標籤最重要的屬性是 href 屬性,它指示連結的目標。
我們將之前獲得的第一章節的URL和 標籤對比看一下:
http://www.biqukan.com/1_1094/5403177.html 第一章 他叫白小純
不難發現, 標籤中href屬性存放的屬性值/1_1094/5403177.html是章節URLhttp://www.biqukan.com/1_1094/5403177.html的後半部分。其他章節也是如此!那這樣,我們就可以根據 標籤的href屬性值獲得每個章節的連結和名稱了。
總結一下:小說每章的連結放在了class屬性為listmain的
# -*- coding:UTF-8 -*- from bs4 import BeautifulSoup import requests if __name__ == "__main__": target = 'http://www.biqukan.com/1_1094/' req = requests.get(url = target) html = req.text div_bf = BeautifulSoup(html) div = div_bf.find_all('div', class_ = 'listmain') print(div[0])
還是使用find_all方法,運行結果如下:
很順利,接下來再匹配每一個標籤,並提取章節名和章節文章。如果我們使用Beautiful Soup匹配到了下面這個標籤,如何提取它的href屬性和標籤裡存放的章節名呢?
第一章 他叫白小純
方法很簡單,對Beautiful Soup返回的匹配結果a,使用a.get('href')方法就能獲取href的屬性值,使用a.string就能獲取章節名,編寫代碼如下:
# -*- coding:UTF-8 -*- from bs4 import BeautifulSoup import requests if __name__ == "__main__": server = 'http://www.biqukan.com/' target = 'http://www.biqukan.com/1_1094/' req = requests.get(url = target) html = req.text div_bf = BeautifulSoup(html) div = div_bf.find_all('div', class_ = 'listmain') a_bf = BeautifulSoup(str(div[0])) a = a_bf.find_all('a') for each in a: print(each.string, server + each.get('href'))
因為find_all返回的是一個列表,裡邊存放了很多的標籤,所以使用for循環遍歷每個標籤並列印出來,運行結果如下。
最上面匹配的一千多章的內容是最新更新的12章節的連結。這12章內容會和下面的重複,所以我們要濾除,除此之外,還有那3個外傳,我們也不想要。這些都簡單地剔除就好。
(3)整合代碼
每個章節的連結、章節名、章節內容都有了。接下來就是整合代碼,將獲得內容寫入文本文件存儲就好了。編寫代碼如下:
很簡單的程序,單進程跑,沒有開進程池。下載速度略慢,喝杯茶休息休息吧。代碼運行效果如下圖所示:
2. 優美壁紙下載
(1)實戰背景
已經會爬取文字了,是不是感覺爬蟲還是蠻好玩的呢?接下來,讓我們進行一個進階實戰,了解一下反爬蟲。URL:https://unsplash.com/
看一看這些優美的壁紙,這個網站的名字叫做Unsplash,免費高清壁紙分享網是一個堅持每天分享高清的攝影圖片的站點,每天更新一張高質量的圖片素材,全是生活中的景象作品,清新的生活氣息圖片可以作為桌面壁紙也可以應用於各種需要的環境。
看到這麼優美的圖片,我的第一反應就是想收藏一些,作為知乎文章的題圖再好不過了。每張圖片我都很喜歡,批量下載吧,不多爬,就下載50張好了。
(2)實戰進階
我們已經知道了每個html標籤都有各自的功能。標籤存放一下超連結,圖片存放在哪個標籤裡呢?html規定,圖片統統給我放到標籤中!既然這樣,我們截取就Unsplash網站中的一個標籤,分析一下:
可以看到,標籤有很多屬性,有alt、src、class、style屬性,其中src屬性存放的就是我們需要的圖片保存地址,我們根據這個地址就可以進行圖片的下載。
那麼,讓我們先捋一捋這個過程:
我們信心滿滿地按照這個思路爬取Unsplash試一試,編寫代碼如下:
# -*- coding:UTF-8 -*- import requests if __name__ == '__main__': target = 'https://unsplash.com/' req = requests.get(url=target) print(req.text)
按照我們的設想,我們應該能找到很多標籤。但是我們發現,除了一些