閱讀文本大概須要 13 分鐘。html
經過以前的文章介紹,你如今應該對 pyspider 有了必定的認識。若是你還不清楚的話,能夠再回顧下以前的文章「高效率爬蟲框架之 pyspider」。務必要對 pysdpier 有個總體認知,這樣你的學習效率纔會高。
如今咱們用一個實戰項目,來進一步掌握 pyspider 框架的使用。這次的項目爬取的目標是「去哪兒網」,我要將全部攻略的做者、標題、出發日期、人均費用、攻略正文等保存下來,存儲到 MongoDB 中。python
請確保已經安裝了 pyspider 和 PhantomJS,安裝好了 MongoDB 並正常運行服務,還須要安裝 PyMongo 庫。這些教程網上都有詳細資料,你們自行搜索。web
執行以下命令就能夠啓動 pyspider:算法
pyspider all
複製代碼
運行效果:
數據庫
這樣能夠啓動 pyspider 的全部組件,包括 PhantomJS、ResultWorker、Processer、Fetcher、Scheduler、WebUI,這些都是 pysipder 運行必備的組件。最後一行輸出 WebUI 運行在 5000 端口上。能夠打開瀏覽器,輸入連接 http://localhost:5000,這時咱們會看到頁面。瀏覽器
此頁面即是 pyspider 的 WebUI,咱們能夠用它來管理項目、編寫代碼、在線調試、監控任務等bash
新建一個項目,點擊右邊的 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 對象作解析,解析以後將結果以字典的形式返回。固然咱們也能夠進行後續處理,如將結果保存到數據庫。
接下來,咱們改寫一下代碼來實現攻略的爬取。
點擊左欄右上角的 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,這就表明咱們成功添加了下一頁的爬取請求。
如今,索引列表頁面的解析過程咱們就完成了。
任意選取一個詳情頁進入,點擊前 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,
}
複製代碼
咱們分別提取了頁面的連接、標題、出行日期、出現天數、人物、攻略正文、頭圖信息,將這些信息構形成一個字典。
從新運行,便可發現輸出結果。
左欄中輸出了最終構造的字典信息,這就是一篇攻略的抓取結果。
返回爬蟲的主頁面,將爬蟲的 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」,你懂得。