什么是網(wǎng)絡(luò)爬蟲?
網(wǎng)絡(luò)爬蟲是一種非常有意思的程序。偌大的Internet,就像是一只蜘蛛織成的大網(wǎng):一個個超級鏈接就是蛛絲,將無數(shù)頁面連接起來,而網(wǎng)絡(luò)爬蟲,則會沿著一根根蛛絲,爬遍每一個節(jié)點(diǎn)……

網(wǎng)絡(luò)爬蟲能干嘛?
蜘蛛在網(wǎng)上爬來爬去,當(dāng)然不是為了健身。它會在網(wǎng)上尋覓獵物,捕捉它們,并拖回自己的窩里。
舉一個例子:某天某日的清晨,老板突然讓你將雪球網(wǎng)上所有的A股行情信息全部保存到一個Excel文件里,以便他瀏覽。你點(diǎn)開了網(wǎng)址雪球(A股行情),驚喜地發(fā)現(xiàn)有三十四頁,一條一條復(fù)制,顯然又累又笨,但是老板要看,你又不得不從,惆悵啊……
很多時候,我們會需要做這樣的工作:將某一類型的文檔全部下載保存下來,然后進(jìn)行分析。一個一個點(diǎn)擊鼠標(biāo),顯然不現(xiàn)實(shí)。那么,就做一個網(wǎng)絡(luò)爬蟲吧,自動化地把需要的東西保存下來。
網(wǎng)絡(luò)爬蟲是怎么干的?
一個簡單的網(wǎng)絡(luò)爬蟲的邏輯并不復(fù)雜,就是模仿人類瀏覽網(wǎng)頁的動作:輸入網(wǎng)址,進(jìn)入頁面,瀏覽網(wǎng)頁,點(diǎn)擊鏈接,進(jìn)入下一頁面……周而復(fù)始。
當(dāng)然,嘴上說是這樣,還得要將步驟細(xì)化一下。所以先來看看瀏覽網(wǎng)頁的時候,我們做了些什么。
當(dāng)我們?yōu)g覽網(wǎng)頁的時候,瀏覽器在做些什么?
蒂姆·伯納斯-李在發(fā)明互聯(lián)網(wǎng)的時候,為了解決頁面之間相互連接的問題,讓網(wǎng)絡(luò)瀏覽更加方便,發(fā)明了超文本標(biāo)記語言HTML。它是一個文本文檔,但是文檔中會有一些特殊的標(biāo)記——標(biāo)簽,它可以標(biāo)記出哪些位置的信息是標(biāo)題,哪些位置的信息是文本,哪些地方是圖像,哪些地方是超級鏈接。它就長這樣。

尖括號中間的就是標(biāo)簽,里面的字就是標(biāo)簽的名字,帶斜杠的表示標(biāo)記結(jié)束了。比如,<p>的意思是段落,</p>的意思就是這一段結(jié)束了。
瀏覽器可以根據(jù)標(biāo)簽,將網(wǎng)頁依照代碼的意思表現(xiàn)出來,用點(diǎn)兒術(shù)語的話,就是瀏覽器是一個HTML的解釋器。當(dāng)我們?yōu)g覽網(wǎng)頁的時候,瀏覽器會先將網(wǎng)頁的HTML文件,以及所需要的其它元素都下載到你的電腦里,然后根據(jù)HTML文件的內(nèi)容,將網(wǎng)頁展現(xiàn)在你的面前。
所以網(wǎng)絡(luò)爬蟲也需要干這件事情:下載HTML文件,然后分析它,根據(jù)分析的結(jié)果將需要的文本或者圖像什么的下載下來,找到超級鏈接,繼續(xù)……
讓我們寫一個網(wǎng)絡(luò)爬蟲,下載頁面上所有的照片吧!
知乎上有很多提問,下面會有很多照片,比如這個怎么用手機(jī)拍出精彩的照片?,圖片都挺好看的,我想全部下載下來,怎么辦呢?打開頁面以后,右鍵點(diǎn)擊頁面,選擇【查看源文件】,就可以看到HTML文件的內(nèi)容了。簡單分析一下,會發(fā)現(xiàn)照片其實(shí)都在這樣的標(biāo)簽下面:

所以,現(xiàn)在就可以制定出我們的策略了:打開頁面,分析它的HTML源文件,找出所有img標(biāo)簽,將里面的圖片都保存下來,over!
用Python實(shí)現(xiàn)你的第一個網(wǎng)絡(luò)爬蟲
Python語言語法簡單而靈活,實(shí)用的庫(別人寫好,可以直接實(shí)用的代碼)非常多,很適合寫這種小爬蟲。所以我們用Python來寫一個網(wǎng)絡(luò)爬蟲??梢渣c(diǎn)擊這里下載Python環(huán)境的安裝包,一直下一步就可以安裝好了。具體學(xué)習(xí)Python可以看這本書,或者這本書。這里我們的代碼并不復(fù)雜,需要的知識點(diǎn)不多,我會一點(diǎn)一點(diǎn)講解的。當(dāng)然你可能需要一點(diǎn)點(diǎn)程序設(shè)計(jì)的知識。
在一個文本編輯器里面輸入以下代碼:
#-*-coding:utf-8-*-
import urllib
url = "https://www.zhihu.com/question/20922273"
page = urllib.urlopen(url)
print page.read()
找一個文件夾,保存為example1.py。然后點(diǎn)擊右鍵選擇【EDIT with IDLE】。

打開以后按F5鍵。應(yīng)該會出現(xiàn)這樣的窗口。

是不是打印出了原來頁面的HTML文件的內(nèi)容?
下面來解釋一下這一段代碼的意思。
#-*-coding:utf-8-*- 的意思是這一段Python程序的編碼格式是UTF-8。理解這一點(diǎn)需要一些編碼的知識,這里暫時不用管它。一般每一個Python程序第一句話都是這個。
import urllib 的意思是導(dǎo)入urllib庫,python自帶的一個網(wǎng)絡(luò)庫,我們可以用它來實(shí)現(xiàn)訪問網(wǎng)頁,下載文件等功能。當(dāng)然還有很多功能更高級的庫,比如urllib2或者requests等,這里我們挑一個簡單的先。
url = "https://www.zhihu.com/question/20922273" 這句話的意思是定義url為字符串"https://www.zhihu.com/question/20922273" ,也就是我們要訪問的網(wǎng)頁的網(wǎng)址,可以看出,網(wǎng)址實(shí)際上是一個字符串變量。
page = urllib.urlopen(url) 這句話的意思是使用urllib庫里面的urlopen函數(shù),打開網(wǎng)址url,并把打開的這個對象命名為page(打開了一個東西,雖然我們不知道它是什么,但是先給它起個名字吧?。?。
page.read() 的意思就是讀取page這個對象的內(nèi)容。print page.read()的意思自然就是打印這個內(nèi)容了。
于是,我們就完成了第一步,打開頁面。下面,我們來寫程序分析打開的頁面。
寫程序,找出所有的圖片地址
在IDLE里面,把前面寫的代碼改一改。
#-*-coding:utf-8-*-
import urllib
from sgmllib import SGMLParser
class ZhihuParser(SGMLParser):
imgList = []
def reset(self):
? ? SGMLParser.reset(self)? #初始化
? ? self.imgList = []
? ? def start_img(self, attrs):
? ? imgUrl = [v for k, v in attrs if k=='data-original']
? ? if imgUrl:
? ? ? ? self.imgList.append(imgUrl[0])
? ? ? ? imgUrl = ""
? ? ? ? url = "https://www.zhihu.com/question/20922273"
? ? ? ? page = urllib.urlopen(url)
? ? ? ? parser = ZhihuParser()
? ? ? ? parser.feed(page.read())
? ? ? ? print parser.imgList
點(diǎn)擊F5運(yùn)行,看看結(jié)果。

下面來解釋一下代碼。
from sgmllib import SGMLParser 這一句的意思是從sgmllib文件里面導(dǎo)入SGMLParser這個包。這個包是Python自帶的一個解析HTML的庫。
class ZhihuParser(SGMLParser):是定義一個類ZhihuParser,它繼承SGMLParser這個類。我們對它進(jìn)行一些修改。
imgList = [] 聲明了一個列表,我們用它來保存所有圖片的地址。
def reset(self): 這個函數(shù)是ZhihuParser類的初始化函數(shù)。每一次生成ZhihuParser類的對象的時候都會調(diào)用這個函數(shù)。我們讓這個函數(shù)先初始化SGMLParser.reset(self),然后把列表制空self.imgList = []。
def start_img(self, attrs): 這個函數(shù)是解析img標(biāo)簽的函數(shù)。當(dāng)SGMLParser遇到一個img標(biāo)簽的時候,就會調(diào)用這個函數(shù)。比如我們要解析<a>標(biāo)簽,那就自己定義一個函數(shù)start_a,解析標(biāo)簽,就定義一個函數(shù)start_head,函數(shù)的內(nèi)容是我們需要的動作。對于這樣的結(jié)束標(biāo)簽,對應(yīng)的函數(shù)是end_something(self)。
start_something函數(shù)的參數(shù)是(self,attrs)。self自然就是類本身,attrs是SGMLParser解析出來的標(biāo)簽參數(shù),比如知乎img的代碼:

里面的src, data-rawwidth等等,都是標(biāo)簽的參數(shù),它以字典的形式傳入函數(shù)中,即一系列參數(shù)名:參數(shù)的二元對。這里我們需要提取出地址,即data-original的內(nèi)容,imgUrl = [v for k, v in attrs if k=='data-original'],意思是遍歷attrs,如果字典的名為data-original,就保存下來。
如果imgUrl保存成功,我們就把它導(dǎo)入到列表里,self.imgList.append(imgUrl[0])。然后將臨時變量imgUrl制空(這句話其實(shí)不必要)。
parser = ZhihuParser()定義了一個ZhihuParser對象,parser.feed(page.read())將頁面的內(nèi)容傳入parser,進(jìn)行解析。print parser.imgList將所有的圖片地址打印出來。
下面我們繼續(xù)修改代碼,把所有圖片保存下來。
在上面的基礎(chǔ)上加入如下代碼:
cnt=1
for?url?in?parser.imgList:
? ? f=open("%d.jpg"%cnt,'wb')
? ? img=urllib.urlopen(url)
? ? f.write(img.read())
? ? f.close()
? ? print?"%d?was?done!"%cnt
? ? cnt?+=?1
cnt = 1是定義一個計(jì)數(shù)器,記錄我們下載圖片的個數(shù)。
for url in parser.imgList:遍歷圖片地址列表。
f = open("%d.jpg"%cnt,'wb')打開一個文件。我們將下載下來的圖片寫入這個文件中。
img = urllib.urlopen(url)打開圖片地址。
f.write(img.read())將圖片寫入文件,然后關(guān)閉文件。
運(yùn)行程序,不一會就看見我們將所有的網(wǎng)頁上的圖片都下載了下來。

于是乎,我們就完成了一個最簡單的網(wǎng)絡(luò)爬蟲。
后 ? ?記
事實(shí)上,真正的通用網(wǎng)絡(luò)爬蟲是很難寫的,要考慮的因素很多。比如怎樣規(guī)避網(wǎng)站的反爬蟲機(jī)制(有的網(wǎng)站可不愿意你下載文件,占用帶寬),怎樣避免爬蟲陷阱(比如有兩個頁面有相互連接的超級鏈接,于是爬蟲就會一直在這兩個頁面間爬來爬去,不去其他頁面),怎樣大規(guī)模快速地爬(分布式爬蟲),怎樣解析JavaScript等等等等。事實(shí)上,網(wǎng)絡(luò)爬蟲是搜索引擎的核心技術(shù)之一,google,百度等搜索引擎公司每天都不停地在網(wǎng)絡(luò)上爬取頁面,解析,并排序,給我們提供搜索服務(wù)。
PS:我不太懂怎么樣在簡書打代碼。。。排版有點(diǎn)丑,請見諒。歡迎來我的Csdn看看,剛開始寫,文章不多。我以后會在上面寫一些我的學(xué)習(xí)筆記。