Python教程:網絡爬蟲快速入門實戰解析

建議: 請在電腦的陪同下,閱讀本文。本文以實戰爲主,閱讀過程如稍有不適,還望多加練習。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

(1)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 信息,有什麼用呢?客官稍安勿躁,接下來進入咱們的實戰正文。

爬蟲實戰

接下來咱們來一次爬蟲實戰,爬取中網小說網站「筆趣看」上的文字。

(1)實戰背景

小說網站-筆趣看:

URL:http://www.biqukan.com/

筆趣看是一個盜版小說網站,這裏有不少起點中文網的小說,該網站小說的更新速度稍滯後於起點中文網正版小說的更新速度。而且該網站只支持在線瀏覽,不支持小說打包下載。所以,本次實戰就是從該網站爬取並保存一本名爲《一念永恆》的小說,該小說是耳根正在連載中的一部玄幻小說。PS:本實例僅爲交流學習,支持耳根大大,請上起點中文網訂閱。

(2)小試牛刀

咱們先看下《一念永恆》小說的第一章內容,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 標籤中提取出來呢?這就是本次實戰的主要內容。

(3)Beautiful Soup

爬蟲的第一步,獲取整個網頁的 HTML 信息,咱們已經完成。接下來就是爬蟲的第二步,解析 HTML 信息,提取咱們感興趣的內容。對於本小節的實戰,咱們感興趣的內容就是文章的正文。提取的方法有不少,例如使用正則表達式、Xpath、Beautiful Soup 等。對於初學者而言,最容易理解,而且使用簡單的方法就是使用 Beautiful Soup 提取感興趣內容。

Beautiful Soup 的安裝方法和 requests 同樣,使用以下指令安裝(也是二選一):

  1. pip install beautifulsoup4
  2. easy_install beautifulsoup4

一個強大的第三方庫,都會有一個詳細的官方文檔。咱們很幸運,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 方法,剔除空格,替換爲回車進行分段。 &nbsp; 在 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 個外傳,咱們也不想要。這些都簡單地剔除就好。

(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 網絡爬蟲快速入門實戰解析

以上就是一次爬蟲實戰。

來和小夥伴們一塊兒向上生長呀!

相關文章
相關標籤/搜索