pyspider 實戰項目之爬取去哪兒

閱讀文本大概須要 13 分鐘。html

經過以前的文章介紹,你如今應該對 pyspider 有了必定的認識。若是你還不清楚的話,能夠再回顧下以前的文章「高效率爬蟲框架之 pyspider」。務必要對 pysdpier 有個總體認知,這樣你的學習效率纔會高。
如今咱們用一個實戰項目,來進一步掌握 pyspider 框架的使用。這次的項目爬取的目標是「去哪兒網」,我要將全部攻略的做者、標題、出發日期、人均費用、攻略正文等保存下來,存儲到 MongoDB 中。python

1 準備工做

請確保已經安裝了 pyspider 和 PhantomJS,安裝好了 MongoDB 並正常運行服務,還須要安裝 PyMongo 庫。這些教程網上都有詳細資料,你們自行搜索。web

2 啓動 pyspider

執行以下命令就能夠啓動 pyspider:算法

pyspider all
複製代碼

運行效果:
數據庫

這樣能夠啓動 pyspider 的全部組件,包括 PhantomJS、ResultWorker、Processer、Fetcher、Scheduler、WebUI,這些都是 pysipder 運行必備的組件。最後一行輸出 WebUI 運行在 5000 端口上。能夠打開瀏覽器,輸入連接 http://localhost:5000,這時咱們會看到頁面。瀏覽器

此頁面即是 pyspider 的 WebUI,咱們能夠用它來管理項目、編寫代碼、在線調試、監控任務等bash

3 建立項目

新建一個項目,點擊右邊的 Create 按鈕,在彈出的浮窗裏輸入項目的名稱和爬取的連接,再點擊 create 按鈕,這樣就成功建立了一個項目。框架

接下來會看到 pyspider 的項目編輯和調試頁面編輯器

左側就是代碼的調試頁面,點擊左側右上角的 run 單步調試爬蟲程序,在左側下半部分能夠預覽當前的爬取頁面。右側是代碼編輯頁面,咱們能夠直接編輯代碼和保存代碼,不須要藉助於 IDE。
注意右側,pyspider 已經幫咱們生成了一段代碼。代碼以下所示:ide

from pyspider.libs.base_handler import *


class Handler(BaseHandler):
    crawl_config = {
    }

    @every(minutes=24 * 60)
    def on_start(self):
        self.crawl('http://travel.qunar.com/travelbook/list.htm', callback=self.index_page)

    @config(age=10 * 24 * 60 * 60)
    def index_page(self, response):
        for each in response.doc('a[href^="http"]').items():
            self.crawl(each.attr.href, callback=self.detail_page)

    @config(priority=2)
    def detail_page(self, response):
        return {
            "url": response.url,
            "title": response.doc('title').text(),
        }
複製代碼

這裏的 Handler 就是 pyspider 爬蟲的主類,咱們能夠在此處定義爬取、解析、存儲的邏輯。整個爬蟲的功能只須要一個 Handler 便可完成。

接下來咱們能夠看到一個 crawl_config 屬性。咱們能夠將本項目的全部爬取配置統必定義到這裏,如定義 Headers、設置代理等,配置以後全局生效。

而後,on_start() 方法是爬取入口,初始的爬取請求會在這裏產生,該方法經過調用 crawl() 方法便可新建一個爬取請求,第一個參數是爬取的 URL,這裏自動替換成咱們定義的 URL。crawl() 方法還有一個 callback,它指定了這個頁面爬取成功後用哪一個方法進行解析,代碼中指定爲 index_page() 方法,即若是這個 URL 對應的頁面爬取成功了,那 Response 將交給 index_page() 方法解析。

index_page() 方法剛好接收這個 Response 參數,Response 對接了 pyquery。咱們直接調用 doc() 方法傳入相應的 CSS 選擇器,就能夠像 pyquery 同樣解析此頁面,代碼中默認是 a[href^="http"],也就是說該方法解析了頁面的全部連接,而後將連接遍歷,再次調用了 crawl() 方法生成了新的爬取請求,同時再指定了 callback 爲 detail_page,意思是說這些頁面爬取成功了就調用 detail_page() 方法解析。這裏,index_page() 實現了兩個功能,一是將爬取的結果進行解析,二是生成新的爬取請求。

detail_page() 一樣接收 Response 做爲參數。detail_page() 抓取的就是詳情頁的信息,就不會生成新的請求,只對 Response 對象作解析,解析以後將結果以字典的形式返回。固然咱們也能夠進行後續處理,如將結果保存到數據庫。

接下來,咱們改寫一下代碼來實現攻略的爬取。

4 爬取首頁

點擊左欄右上角的 run 按鈕,便可看到頁面下方 follows 便會出現一個標註,其中包含數字 1 ,這表明有新的爬取請求產生。

左欄左上角會出現當前 run 的配置文章,這裏有一個 callback 爲 on_start,這說明點擊 run 以後實際是執行了 on_start() 方法。在 on_start() 方法中,咱們利用 crawl() 方法生成一個爬取請求,那下方 follows 部分的數字 1 就表明了這一個爬取請求。

點擊下方的 follows 按鈕,便可看到生成的爬取請求的連接。每一個連接的右側還有一個箭頭按鈕。

點擊該箭頭,咱們就能夠對此連接進行爬取,也就是爬取攻略的首頁內容。

上方的 callback 已經變成了 index_page,這就表明當前運行了 index_page() 方法。index_page() 接收到的 response 參數就是剛纔生成的第一個爬取請求的 Response 對象。index_page() 方法經過調用 doc() 方法,傳入提取全部 a 節點的 CSS 選擇器,而後獲取 a 節點的屬性 href,這樣實際上就是獲取了第一個爬取頁面中的全部連接。而後在 index_page() 方法裏遍歷了全部連接,同時調用 crawl() 方法,就把這一個個的連接構形成新的爬取請求了。因此最下方 follows 按鈕部分有 231 的數字標記,這表明新生成了 231 個爬取請求,同時這些請求的 URL 都呈如今當前的頁面了。

再點擊下方的 web 按鈕,便可預覽當前爬取結果的頁面。

這裏編輯器並非很友好,顯示的頁面只有一小些,但並不會妨礙咱們的抓取。當前看到的頁面結果和瀏覽器看到的幾乎是徹底一致的,在這裏咱們能夠方便地查看頁面請求的結果。

點擊 html 按鈕便可查看當前頁面的源代碼。

咱們剛纔在 index_page() 方法中提取了全部的連接並生成了新的爬取請求。可是很明顯要爬取的確定不是全部連接,只須要攻略詳情的頁面連接就夠了,因此咱們要修改一下當前 index_page() 裏提取連接時的 CSS 選擇器。

在右側代碼選中要更改的區域,點擊左欄的右箭頭,此時在上方出現的標題的 CSS 選擇器就會被替換到右側代碼中。

這樣就成功完成了 CSS 選擇器的替換,很是方便。
從新點擊左欄右上角的 run 按鈕,便可從新執行 index_page() 方法。此時的 follows 就變成了 10 個,也就是說如今咱們提取的只有當前頁面的 10 個攻略。

咱們如今抓取的只是第一頁的內容,還須要抓取後續頁面,因此還須要一個爬取連接,即爬取下一頁的攻略列表頁面。咱們再利用 crawl() 方法添加下一頁的爬取請求,在 index_page() 方法裏面添加以下代碼,而後點擊 save() 保存。

next = response.doc('.next').attr.href
self.crawl(next, callback=self.index_page)
複製代碼

利用 CSS 選擇器選中下一頁的連接,獲取它的 href 屬性,也就獲取了頁面的 URL。而後將該 URL 傳給 crawl() 方法,同時指定回調函數,注意這裏回調函數仍然指定爲 index_page() 方法,由於下一頁的結構與此頁相同。

從新點擊 run 按鈕,這時就能夠看到 11 個爬取請求。follows 按鈕上會顯示 11,這就表明咱們成功添加了下一頁的爬取請求。

如今,索引列表頁面的解析過程咱們就完成了。

5 爬取詳情頁

任意選取一個詳情頁進入,點擊前 10 個爬取請求的任意一個的右箭頭,執行詳情頁的爬取。

切換到 Web 頁面預覽效果,頁面下拉以後,頭圖正文中的一些圖片一直顯示加載中。

查看源碼,咱們沒有看到 img 節點。

出現此現象的緣由是 pyspider 默認發送 HTTP 請求,請求的 HTML 文檔自己就不包含 img 節點。可是在瀏覽器中咱們看到了圖片,這是由於這張圖片是後期通過 JavaScrpit 出現的。那麼,咱們該如何獲取呢?

幸運的是,pyspider 內部對接了 PhatomJS,那麼咱們只須要修改一個參數便可。

咱們將 index_page() 中生成抓取詳情頁的請求方法添加一個參數 fetch_type,改寫的 index_page() 變爲以下內容:

def index_page(self, response):
        for each in response.doc('li > .tit > a').items():
            self.crawl(each.attr.href, callback=self.detail_page, fetch_type='js')
        next = response.doc('.next').attr.href
        self.crawl(next, callback=self.index_page)
複製代碼

接下來,咱們來試試它的抓取效果。

點擊左欄上方的左箭頭返回,從新調用 index_page() 方法生成新的爬取詳情頁的 Request。

再點擊新生成的詳情頁的 Request 的爬取按鈕,這時咱們即可以看到頁面變成了這樣子。

圖片被成功渲染處理,這就是啓用了 PhantomJS 渲染後的結果。只須要添加一個 fetch_type 參數便可,這很是方便。
最後就是將詳情頁面中須要的信息提取處理。最終的 detail_page() 方法改寫以下:

def detail_page(self, response):
        return {
            "url": response.url,
            "title": response.doc('#booktitle').text(),
            "date": response.doc('.when .data').text(),
            "day": response.doc('.howlong .data').text(),
            "who": response.doc('.who .data').text(),
            "text": response.doc('#b_panel_schedule').text(),
            "image": response.doc('.cover_img').attr.src,
        }
複製代碼

咱們分別提取了頁面的連接、標題、出行日期、出現天數、人物、攻略正文、頭圖信息,將這些信息構形成一個字典。
從新運行,便可發現輸出結果。

左欄中輸出了最終構造的字典信息,這就是一篇攻略的抓取結果。

6 啓動爬蟲

返回爬蟲的主頁面,將爬蟲的 status 設置成 DEBUG 或 RUNNING,點擊右側的 Run 按鈕便可開始爬取。

在最左側咱們能夠定義項目的分組,以方便管理。rate/burst 表明當前的爬取速率。rate 表明 1 秒發出多少個請求,burst 至關於流量控制中的令牌桶算法的令牌數,rate 和 burst 設置的越大,爬取速率越快,固然速率須要考慮本機性能和爬取過快被封的問題。process 中的 5m、1h、1d 指 的是最近 5 分、1 小時、1 天內的請求狀況,all 表明全部的請求狀況。請求由不一樣顏色表示、藍色的表明等待被執行的請求,綠色的表明成功的請求,黃色的表明請求失敗後等待重試的請求,紅色的表明失敗次數過多而被忽略的請求,這樣能夠直觀知道爬取的進度和請求狀況。

點擊 Active Tasks,便可查看最近請求的詳細情況。

點擊 Result,便可查看全部的爬取結果。

點擊右上角的按鈕,便可獲取數據的 JSON、CSV 格式。

本文首發於公衆號「癡海」,天天分享 python 乾貨,回覆「1024」,你懂得。


相關文章
相關標籤/搜索