最近看過不少講爬蟲的教程[1][2],基本都是一個(gè)模式:

  1. 開始先來拿正則、lxml、jquery/pyquery等等教大家從頁面上摳出一個(gè)一個(gè)的值來

  2. 然后深入一些在講講http 協(xié)議,講講怎么拿出 cookie 來模擬登錄之類的,講講基本的反爬蟲和反反爬蟲的方法

  3. 最后在上一個(gè) 簡(jiǎn)單地 scrapy 教程,似乎就皆大歡喜了。

具體地采集一個(gè)一個(gè)的數(shù)據(jù)的確讓人產(chǎn)生成就感,然而這些教程卻都忽略了爬蟲最核心的邏輯抽象,也就是「爬蟲應(yīng)該采取什么樣的策略遍歷網(wǎng)頁」。其實(shí)也很簡(jiǎn)單,只需要兩個(gè)隊(duì)列和一個(gè)集合,Scrapy 等框架拆開來看也是如此,本文參照 Scrapy 實(shí)現(xiàn)一個(gè)最基礎(chǔ)的通用爬蟲。

萬維網(wǎng)是由一個(gè)一個(gè)的頁面構(gòu)成的,而每個(gè)頁面和頁面之間是由鏈接來聯(lián)系的,并且這些鏈接都是具有方向性的。對(duì)應(yīng)到數(shù)據(jù)結(jié)構(gòu)的話,我們可以把每一個(gè)頁面都看作一個(gè)節(jié)點(diǎn),而每一個(gè)鏈接都是一個(gè)有向邊,也就是整個(gè)萬維網(wǎng)其實(shí)是一個(gè)巨大的「有向圖」[3]。說到這里,可能有的同學(xué)已經(jīng)明白了,可以用廣度優(yōu)先或者深度優(yōu)先的算法來遍歷這個(gè)圖。當(dāng)然,這個(gè)圖是在太巨大了,我們不可能遍歷整個(gè)圖,而是加一些限定條件,只去訪問其中很小一部分我們感興趣的節(jié)點(diǎn),比如某個(gè)域名下的網(wǎng)頁。

廣度優(yōu)先和深度優(yōu)先都可以使用遞歸或者輔助的隊(duì)列(queue/lifo_queue)來實(shí)現(xiàn)。然而如果你的爬蟲是用 python 寫的話,很遺憾不能使用遞歸來實(shí)現(xiàn)了,原因很簡(jiǎn)單,我們要訪問的網(wǎng)頁可能成千上萬,如果采用遞歸來實(shí)現(xiàn),那么爬蟲每向前訪問一個(gè)節(jié)點(diǎn),系統(tǒng)的調(diào)用棧就會(huì) +1,而 python 中至今沒有尾遞歸優(yōu)化,默認(rèn)的堆棧深度為1000,也就是很可能你訪問了1000個(gè)網(wǎng)頁之后就拋出異常了。所以我們這里使用隊(duì)列實(shí)現(xiàn)對(duì)網(wǎng)頁的遍歷訪問。

理論知識(shí)說了這么多,下面以一個(gè)例子來說明一下如何爬取數(shù)據(jù):爬取煎蛋網(wǎng)的妹子圖: http://jandan.net/ooxx

首先,我們打開對(duì)應(yīng)的網(wǎng)址,作為起始頁面,也就是把這個(gè)頁面放入待訪問的頁面的隊(duì)列。注意,這是我們需要的第一個(gè)隊(duì)列,存放我們的待訪問頁面。

class MiniSpider(object):

    def __init__(self):
         self._request_queue = queue.Queue()  # 帶請(qǐng)求頁面的隊(duì)列
         self._request_queue.put('http://jandan.net/ooxx')  # 把第一個(gè)待訪問頁面入隊(duì)

延伸閱讀

學(xué)習(xí)是年輕人改變自己的最好方式-Java培訓(xùn),做最負(fù)責(zé)任的教育,學(xué)習(xí)改變命運(yùn),軟件學(xué)習(xí),再就業(yè),大學(xué)生如何就業(yè),幫大學(xué)生找到好工作,lphotoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)學(xué)習(xí)是年輕人改變自己的最好方式