建議: 請在電腦的陪同下,閱讀本文。本文以實戰爲主,閱讀過程如稍有不適,還望多加練習。html
網絡爬蟲,也叫網絡蜘蛛(Web Spider)。它根據網頁地址(URL)爬取網頁內容,而網頁地址(URL)就是咱們在瀏覽器中輸入的網站連接。好比:https://www.baidu.com/,它就是一個 URL。python
在講解爬蟲內容以前,咱們須要先學習一項寫爬蟲的必備技能: 審查元素(若是已掌握,可跳過此部份內容) 。git
在瀏覽器的地址欄輸入 URL 地址,在網頁處右鍵單擊,找到檢查。(不一樣瀏覽器的叫法不一樣,Chrome 瀏覽器叫作檢查,Firefox 瀏覽器叫作查看元素,可是功能都是相同的)github
Python3 網絡爬蟲快速入門實戰解析正則表達式
咱們能夠看到,右側出現了一大推代碼,這些代碼就叫作 HTML。什麼是 HTML?舉個容易理解的例子: 咱們的基因決定了咱們的原始容貌,服務器返回的 HTML 決定了網站的原始容貌。瀏覽器
Python3 網絡爬蟲快速入門實戰解析安全
爲啥說是 原始容貌 呢?由於人能夠整容啊!扎心了,有木有? 那網站也能夠"整容"嗎?能夠!請看下圖:服務器
Python3 網絡爬蟲快速入門實戰解析網絡
我能有這麼多錢嗎?顯然不可能。我是怎麼給網站"整容"的呢?就是經過修改服務器返回的 HTML 信息。咱們每一個人都是"整容大師",能夠修改頁面信息。 咱們在頁面的哪一個位置點擊審查元素,瀏覽器就會爲咱們定位到相應的 HTML 位置,進而就能夠在本地更改 HTML 信息。app
再舉個小例子:咱們都知道,使用瀏覽器"記住密碼"的功能,密碼會變成一堆小黑點,是不可見的。可讓密碼顯示出來嗎?能夠,只需給頁面"動個小手術"!以淘寶爲例,在輸入密碼框處右鍵,點擊檢查。
Python3 網絡爬蟲快速入門實戰解析
能夠看到,瀏覽器爲咱們自動定位到了相應的 HTML 位置。將下圖中的 password 屬性值改成 text 屬性值( 直接在右側代碼處修改 ):
Python3 網絡爬蟲快速入門實戰解析
咱們讓瀏覽器記住的密碼就這樣顯現出來了:
Python3 網絡爬蟲快速入門實戰解析
說這麼多,什麼意思呢? 瀏覽器就是做爲客戶端從服務器端獲取信息,而後將信息解析,並展現給咱們的。 咱們能夠在本地修改 HTML 信息,爲網頁"整容",可是咱們修改的信息不會回傳到服務器,服務器存儲的 HTML 信息不會改變。刷新一下界面,頁面還會回到本來的樣子。 這就跟人整容同樣,咱們能改變一些表面的東西,可是不能改變咱們的基因。
網絡爬蟲的第一步就是根據 URL,獲取網頁的 HTML 信息。在 Python3 中,可使用 urllib.request 和 requests 進行網頁爬取。
urllib 庫是 python 內置的,無需咱們額外安裝,只要安裝了 Python 就可使用這個庫。
requests 庫是第三方庫,須要咱們本身安裝。這個庫強大好用,因此本文使用 requests 庫獲取網頁的 HTML 信息。requests 庫的 github 地址:https://github.com/requests/requests
在 cmd 中,使用以下指令安裝 requests:
1pip install requests
requests 庫的基礎方法以下:
Python3 網絡爬蟲快速入門實戰解析
官方中文教程地址:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html
requests 庫的開發者爲咱們提供了詳細的中文教程,查詢起來很方便。本文不會對其全部內容進行講解,摘取其部分使用到的內容,進行實戰說明。
首先,讓咱們看下 requests.get()方法,它用於向服務器發起 GET 請求,不瞭解 GET 請求沒有關係。咱們能夠這樣理解:get 的中文意思是獲得、抓住,那這個 requests.get()方法就是從服務器獲得、抓住數據,也就是獲取數據。讓咱們看一個例子(以 www.gitbook.cn 爲例)來加深理解:
1# -*- coding:UTF-8 -*- 2import requests 3 4if __name__ == '__main__': 5 target = 'http://gitbook.cn/' 6 req = requests.get(url=target) 7 print(req.text)
requests.get()方法必須設置的一個參數就是 url,由於咱們得告訴 GET 請求,咱們的目標是誰,咱們要獲取誰的信息。運行程序看下結果:
Python3 網絡爬蟲快速入門實戰解析
左側是咱們程序得到的結果,右側是咱們在 www.gitbook.cn 網站審查元素得到的信息。咱們能夠看到,咱們已經順利得到了該網頁的 HTML 信息。這就是一個最簡單的爬蟲實例,可能你會問,我只是爬取了這個網頁的 HTML 信息,有什麼用呢?客官稍安勿躁,接下來進入咱們的實戰正文。
接下來咱們來一次爬蟲實戰,爬取中網小說網站「筆趣看」上的文字。
小說網站-筆趣看:
URL:http://www.biqukan.com/
筆趣看是一個盜版小說網站,這裏有不少起點中文網的小說,該網站小說的更新速度稍滯後於起點中文網正版小說的更新速度。而且該網站只支持在線瀏覽,不支持小說打包下載。所以,本次實戰就是從該網站爬取並保存一本名爲《一念永恆》的小說,該小說是耳根正在連載中的一部玄幻小說。PS:本實例僅爲交流學習,支持耳根大大,請上起點中文網訂閱。
咱們先看下《一念永恆》小說的第一章內容,URL:http://www.biqukan.com/1_1094/5403177.html
Python3 網絡爬蟲快速入門實戰解析
咱們先用已經學到的知識獲取 HTML 信息試一試,編寫代碼以下:
1# -*- coding:UTF-8 -*- 2import requests 3 4if __name__ == '__main__': 5 target = 'http://www.biqukan.com/1_1094/5403177.html' 6 req = requests.get(url=target) 7 print(req.text)
運行代碼,能夠看到以下結果:
Python3 網絡爬蟲快速入門實戰解析
能夠看到,咱們很輕鬆地獲取了 HTML 信息。可是,很顯然,不少信息是咱們不想看到的,咱們只想得到如右側所示的正文內容,咱們不關心 div、br 這些 html 標籤。 如何把正文內容從這些衆多的 html 標籤中提取出來呢?這就是本次實戰的主要內容。
爬蟲的第一步,獲取整個網頁的 HTML 信息,咱們已經完成。接下來就是爬蟲的第二步,解析 HTML 信息,提取咱們感興趣的內容。對於本小節的實戰,咱們感興趣的內容就是文章的正文。提取的方法有不少,例如使用正則表達式、Xpath、Beautiful Soup 等。對於初學者而言,最容易理解,而且使用簡單的方法就是使用 Beautiful Soup 提取感興趣內容。
Beautiful Soup 的安裝方法和 requests 同樣,使用以下指令安裝(也是二選一):
一個強大的第三方庫,都會有一個詳細的官方文檔。咱們很幸運,Beautiful Soup 也是有中文的官方文檔。URL:
http://beautifulsoup.readthedocs.io/zh_CN/latest/
同理,我會根據實戰需求,講解 Beautiful Soup 庫的部分使用方法,更詳細的內容,請查看官方文檔。
如今,咱們使用已經掌握的審查元素方法,查看一下咱們的目標頁面,你會看到以下內容:
Python3 網絡爬蟲快速入門實戰解析
不難發現,文章的全部內容都放在了一個名爲 div 的「東西下面」,這個"東西"就是 html 標籤。HTML 標籤是 HTML 語言中最基本的單位,HTML 標籤是 HTML 最重要的組成部分。不理解,不要緊,咱們再舉個簡單的例子:
一個女人的包包裏,會有不少東西,她們會根據本身的習慣將本身的東西進行分類放好。鏡子和口紅這些會常常用到的東西,會歸放到容易拿到的外側口袋裏。那些不常常用到,須要注意安全存放的證件會放到不容易拿到的裏側口袋裏。
html 標籤就像一個個「口袋」,每一個「口袋」都有本身的特定功能,負責存放不一樣的內容。顯然,上述例子中的 div 標籤下存放了咱們關心的正文內容。這個 div 標籤是這樣的:
1<div id="content", class="showtxt">
細心的朋友可能已經發現,除了 div 字樣外,還有 id 和 class。id 和 class 就是 div 標籤的屬性,content 和 showtxt 是屬性值,一個屬性對應一個屬性值。這東西有什麼用?它是用來區分不一樣的 div 標籤的,由於 div 標籤能夠有不少,咱們怎麼加以區分不一樣的 div 標籤呢?就是經過不一樣的屬性值。
仔細觀察目標網站一番,咱們會發現這樣一個事實: class 屬性爲 showtxt 的 div 標籤,獨一份!這個標籤裏面存放的內容,是咱們關心的正文部分。
知道這個信息,咱們就可使用 Beautiful Soup 提取咱們想要的內容了,編寫代碼以下:
1# -*- coding:UTF-8 -*- 2from bs4 import BeautifulSoup 3import requests 4if __name__ == "__main__": 5 target = 'http://www.biqukan.com/1_1094/5403177.html' 6 req = requests.get(url = target) 7 html = req.text 8 bf = BeautifulSoup(html) 9 texts = bf.find_all('div', class_ = 'showtxt') 10 print(texts)
在解析 html 以前,咱們須要建立一個 Beautiful Soup 對象。BeautifulSoup 函數裏的參數就是咱們已經得到的 html 信息。而後咱們使用 find_all 方法,得到 html 信息中全部 class 屬性爲 showtxt 的 div 標籤。 find_all 方法的第一個參數是獲取的標籤名,第二個參數 class_ 是標籤的屬性,爲何不是 class,而帶了一個下劃線呢?由於 python 中 class 是關鍵字,爲了防止衝突,這裏使用 class_ 表示標籤的 class 屬性, class_ 後面跟着的 showtxt 就是屬性值了。看下咱們要匹配的標籤格式:
1<div id="content", class="showtxt">
這樣對應的看一下,是否是就懂了?可能有人會問了,爲何不是 find_all('div', id = 'content', class_ = 'showtxt') ?這樣其實也是能夠的,屬性是做爲查詢時候的約束條件,添加一個 class_='showtxt' 條件,咱們就已經可以準確匹配到咱們想要的標籤了,因此咱們就沒必要再添加 id 這個屬性了。運行代碼查看咱們匹配的結果:
Python3 網絡爬蟲快速入門實戰解析
咱們能夠看到,咱們已經順利匹配到咱們關心的正文內容,可是還有一些咱們不想要的東西。好比 div 標籤名,br 標籤,以及各類空格。怎麼去除這些東西呢?咱們繼續編寫代碼:
1# -*- coding:UTF-8 -*- 2from bs4 import BeautifulSoup 3import requests 4if __name__ == "__main__": 5 target = 'http://www.biqukan.com/1_1094/5403177.html' 6 req = requests.get(url = target) 7 html = req.text 8 bf = BeautifulSoup(html) 9 texts = bf.find_all('div', class_ = 'showtxt') 10 print(texts[0].text.replace('\xa0'*8,'\n\n'))
find_all 匹配的返回的結果是一個列表。提取匹配結果後,使用 text 屬性,提取文本內容,濾除 br 標籤。隨後使用 replace 方法,剔除空格,替換爲回車進行分段。 在 html 中是用來表示空格的。 replace('\xa0'*8,'\n\n') 就是去掉下圖的八個空格符號,並用回車代替:
Python3 網絡爬蟲快速入門實戰解析
程序運行結果以下:
Python3 網絡爬蟲快速入門實戰解析
能夠看到,咱們很天然的匹配到了全部正文內容,並進行了分段。咱們已經順利得到了一個章節的內容,要想下載正本小說,咱們就要獲取每一個章節的連接。咱們先分析下小說目錄:URL:http://www.biqukan.com/1_1094/
Python3 網絡爬蟲快速入門實戰解析
經過審查元素,咱們發現能夠發現,這些章節都存放在了 class 屬性爲 listmain 的 div 標籤下,選取部分 html 代碼以下:
1<div class="listmain"> 2<dl> 3<dt>《一念永恆》最新章節列表</dt> 4<dd><a href="/1_1094/15932394.html">第 1027 章 第十道門</a></dd> 5<dd><a href="/1_1094/15923072.html">第 1026 章 絕倫道法!</a></dd> 6<dd><a href="/1_1094/15921862.html">第 1025 章 長生燈!</a></dd> 7<dd><a href="/1_1094/15918591.html">第 1024 章 一目晶淵</a></dd> 8<dd><a href="/1_1094/15906236.html">第 1023 章 通天道門</a></dd> 9<dd><a href="/1_1094/15903775.html">第 1022 章 四大凶獸!</a></dd> 10<dd><a href="/1_1094/15890427.html">第 1021 章 鱷首!</a></dd> 11<dd><a href="/1_1094/15886627.html">第 1020 章 一觸即發!</a></dd> 12<dd><a href="/1_1094/15875306.html">第 1019 章 魁祖的氣息!</a></dd> 13<dd><a href="/1_1094/15871572.html">第 1018 章 絕望的魁皇城</a></dd> 14<dd><a href="/1_1094/15859514.html">第 1017 章 我仍是恨你!</a></dd> 15<dd><a href="/1_1094/15856137.html">第 1016 章 歷來沒有世界之門!</a></dd> 16<dt>《一念永恆》正文卷</dt> <dd><a href="/1_1094/5386269.html">外傳 1 柯父。</a></dd> 17<dd><a href="/1_1094/5386270.html">外傳 2 楚玉嫣。</a></dd> <dd><a href="/1_1094/5386271.html">外傳 3 鸚鵡與皮凍。</a></dd> 18<dd><a href="/1_1094/5403177.html">第一章 他叫白小純</a></dd> <dd><a href="/1_1094/5428081.html">第二章 火竈房</a></dd> 19<dd><a href="/1_1094/5433843.html">第三章 六句真言</a></dd> <dd><a href="/1_1094/5447905.html">第四章 煉靈</a></dd> 20</dl> 21</div>
在分析以前,讓咱們先介紹一個概念:父節點、子節點、孫節點。 <div> 和 </div> 限定了<div> 標籤的開始和結束的位置,他們是成對出現的,有開始位置,就有結束位置。咱們能夠看到,在 <div> 標籤包含 <dl> 標籤,那這個 <dl> 標籤就是 <div> 標籤的子節點, <dl> 標籤又包含 <dt> 標籤和 <dd> 標籤,那麼 <dt> 標籤和 <dd> 標籤就是 <div> 標籤的孫節點。有點繞?那你記住這句話: 誰包含誰,誰就是誰兒子!
他們之間的關係都是相對的。好比對於 <dd> 標籤,它的子節點是 <a> 標籤,它的父節點是<dl> 標籤。這跟咱們人是同樣的,上有老下有小。
看到這裏可能有人會問,這有好多 <dd> 標籤和 <a> 標籤啊!不一樣的 <dd> 標籤,它們是什麼關係啊?顯然,兄弟姐妹嘍!咱們稱它們爲兄弟結點。
好了,概念明確清楚,接下來,讓咱們分析一下問題。咱們看到每一個章節的名字存放在了 <a>標籤裏面。 <a> 標籤還有一個 href 屬性。這裏就不得不提一下 <a> 標籤的定義了, <a> 標籤訂義了一個超連接,用於從一張頁面連接到另外一張頁面。 <a> 標籤最重要的屬性是 href 屬性,它指示連接的目標。
咱們將以前得到的第一章節的 URL 和 <a> 標籤對比看一下:
1http://www.biqukan.com/1_1094/5403177.html
不難發現, <a> 標籤中 href 屬性存放的屬性值 /1_1094/5403177.html 是章節 URL http://www.biqukan.com/1_1094/5403177.html 的後半部分。其餘章節也是如此!那這樣,咱們就能夠根據 <a> 標籤的 href 屬性值得到每一個章節的連接和名稱了。
總結一下:小說每章的連接放在了 class 屬性爲 listmain 的 <div> 標籤下的 <a> 標籤中。連接具體位置放在 html->body->div->dl->dd->a 的 href 屬性中。先匹配 class 屬性爲 listmain 的 <div> 標籤,再匹配 <a> 標籤。編寫代碼以下:
1# -*- coding:UTF-8 -*- 2from bs4 import BeautifulSoup 3import requests 4if __name__ == "__main__": 5 target = 'http://www.biqukan.com/1_1094/' 6 req = requests.get(url = target) 7 html = req.text 8 div_bf = BeautifulSoup(html) 9 div = div_bf.find_all('div', class_ = 'listmain') 10 print(div[0])
仍是使用 find_all 方法,運行結果以下:
Python3 網絡爬蟲快速入門實戰解析
很順利,接下來再匹配每個 <a> 標籤,並提取章節名和章節文章。若是咱們使用 Beautiful Soup 匹配到了下面這個 <a> 標籤,如何提取它的 href 屬性和 <a> 標籤裏存放的章節名呢?
1<a href="/1_1094/5403177.html">第一章 他叫白小純</a>
方法很簡單,對 Beautiful Soup 返回的匹配結果 a,使用 a.get('href')方法就能獲取 href 的屬性值,使用 a.string 就能獲取章節名,編寫代碼以下:
1# -*- coding:UTF-8 -*- 2from bs4 import BeautifulSoup 3import requests 4if __name__ == "__main__": 5 server = 'http://www.biqukan.com/' 6 target = 'http://www.biqukan.com/1_1094/' 7 req = requests.get(url = target) html = req.text 8 div_bf = BeautifulSoup(html) 9 div = div_bf.find_all('div', class_ = 'listmain') 10 a_bf = BeautifulSoup(str(div[0])) 11 a = a_bf.find_all('a') 12 for each in a: 13 print(each.string, server + each.get('href'))
由於 find_all 返回的是一個列表,裏邊存放了不少的 <a> 標籤,因此使用 for 循環遍歷每一個 <a> 標籤並打印出來,運行結果以下:
Python3 網絡爬蟲快速入門實戰解析
最上面匹配的一千多章的內容是最新更新的 12 章節的連接。這 12 章內容會和下面的重複,因此咱們要濾除,除此以外,還有那 3 個外傳,咱們也不想要。這些都簡單地剔除就好。
每一個章節的連接、章節名、章節內容都有了。接下來就是整合代碼,將得到內容寫入文本文件存儲就行了。編寫代碼以下:
1# -*- coding:UTF-8 -*- 2from bs4 import BeautifulSoup 3import requests, sys 4 5class downloader(object): 6 def __init__(self): 7 self.server = 'http://www.biqukan.com/' 8 self.target = 'http://www.biqukan.com/1_1094/' 9 self.names = [] #存放章節名 10 self.urls = [] #存放章節連接 11 self.nums = 0 #章節數 12 13 def get_download_url(self): 14 req = requests.get(url = self.target) 15 html = req.text 16 div_bf = BeautifulSoup(html) 17 div = div_bf.find_all('div', class_ = 'listmain') 18 a_bf = BeautifulSoup(str(div[0])) 19 a = a_bf.find_all('a') 20 self.nums = len(a[15:]) #剔除沒必要要的章節,並統計章節數 21 for each in a[15:]: 22 self.names.append(each.string) 23 self.urls.append(self.server + each.get('href')) 24 25 """ 26 函數說明:獲取章節內容 27 Parameters: 28 target - 下載鏈接(string) 29 Returns: 30 texts - 章節內容(string) 31 """ 32 def get_contents(self, target): 33 req = requests.get(url = target) 34 html = req.text 35 bf = BeautifulSoup(html) 36 texts = bf.find_all('div', class_ = 'showtxt') 37 texts = texts[0].text.replace('\xa0'*8,'\n\n') 38 return texts 39 40 """ 41 函數說明:將爬取的文章內容寫入文件 42 Parameters: 43 name - 章節名稱(string) 44 path - 當前路徑下,小說保存名稱(string) 45 text - 章節內容(string) 46 Returns: 47 無 48 """ 49 def writer(self, name, path, text): 50 write_flag = True 51 with open(path, 'a', encoding='utf-8') as f: 52 f.write(name + '\n') 53 f.writelines(text) 54 f.write('\n\n') 55 56if __name__ == "__main__": 57 dl = downloader() 58 dl.get_download_url() 59 print('《一年永恆》開始下載:') 60 for i in range(dl.nums): 61 dl.writer(dl.names[i], '一念永恆.txt', dl.get_contents(dl.urls[i])) 62 sys.stdout.write(" 已下載:%.3f%%" % float(i/dl.nums*100) + '\r') 63 sys.stdout.flush() 64 print('《一年永恆》下載完成')
很簡單的程序,單進程跑,沒有開進程池。下載速度略慢,喝杯茶休息休息吧。代碼運行效果以下圖所示:
Python3 網絡爬蟲快速入門實戰解析
以上就是一次爬蟲實戰。
來和小夥伴們一塊兒向上生長呀!