當前位置:
首頁 > 最新 > Scrapy爬蟲最佳實踐:一個高效的「知乎」爬蟲

Scrapy爬蟲最佳實踐:一個高效的「知乎」爬蟲

主要內容

本文將較為詳實地介紹如何設計一個完整的爬蟲,旨在為一般性問題提供指導。

下面將在Mac OS X、Python 3.6.1、PyCharm 2017.3.1(Professional Edition)、MySQL環境下,基於Scrapy,以爬取「知乎」(zhihu.com)「話題-問題-回答-評論-用戶」數據為例,對資料庫設計、爬蟲框架構建、網頁解析、數據存儲、反反爬蟲策略進行介紹。

創建Scrapy項目

Scrapy是一種非阻塞(即非同步)的爬蟲框架,主要由Scrapy Engine、Schedule、Downloader、Spiders、Item Pipeline和中間件Downloader Middlewares、Spider Middlewares這「5+2」個部分構成。此?前在《爬蟲進階:Scrapy框架實戰——以爬取微公益「個人求助」項目數據為例》一文中對Scrapy進行過簡要介紹。

首先,需要創建一個Scrapy工程。這裡選擇在本地創建一個名為DS_Scrapy的Project,因此,打開terminal(終端)並鍵入scrapy startproject DS_Scrapy。接著,進入DS_Scrapy目錄(cd DS_Scrapy),鍵入scrapy genspider DS_Zhihu zhihu.com(zhihu.com為爬取網站的域名),以創建一個名為DS_Zhihu的Spider(爬蟲)。此時,我們將得到一個組織結構如下的文件(後文將結合具體問題逐一編寫各個模塊),為方便編寫,現在用PyCharm打開該Project。

「知乎」概覽及資料庫設計

「知乎」(zhihu.com)

首先,分析網站的數據組織情況。這裡我們簡單瀏覽一下Q&A sites(知識問答網站)的「Topic—Question—Answer」(主題—問題—回答)框架結構。以下依次展示「南京大學」這一主題的「主題」、「問題」和「回答」頁面。

此外,用戶可以對每個「問題」和「回答」進行評論,因此,我們除了「話題」、「問題」、「回答」外,還需要抓取「評論」數據,以及它們的生成者(Generator)——「用戶」的數據。「用戶」頁面如下所示。

此前,在《知乎數據抓取邏輯PPT分享》一文中介紹過如何基於Selenium模擬瀏覽器的方式抓取「知乎」數據,因此,本文希望以另一種方式,即通過Ajax來獲取「知乎」數據,具體可以通過瀏覽器的「開發者工具」找到對應的介面(如何找請自行Google/百度)。下圖為問題「第四輪學科評估,哪些高校後來居上,哪些高校將滑落?」的一個Request URL及其返回的結果(json格式)。所以接下來的問題就是如何根據URL構造規律(一般由不同參數控制)以獲取所有數據以及如何保存json數據(網頁解析)。

在對全站分析後,得到獲取數據的7大入口,其分別對應「主題信息」、「主題下所有問題」、「問題信息」、「問題下所有回答」、「問題的評論」、「回答的評論」以及「用戶信息」這7部分數據。

資料庫設計

分析不同部分的數據,儘可能多地將現在需要及未來可能需要的數據信息保存到資料庫中。

本文具體介紹如何通過PyMySQL來操作MySQL。首先,通過Navicat for MySQL新建一個名為research_zhihuDatabase(資料庫),這裡將user和password設為root。然後,我們在DS_Scrapy中新建一個名為SQL_table的文件夾,並在該文件夾中創建一個用於存放sql語句的sql.py文件和一個用於執行sql語句的mysql.py文件。

下面將展示sql.py的部分代碼。其中存放了7條sql語句,分別用於創建不同的table(表),以存放約200個數據項。一個需要注意的問題是Charset編碼問題,這裡選用utf8mb4是為了存儲4個位元組的表情(utf8無法滿足存儲要求)。如果你在設置utf8mb4時報錯,請從網上自行尋找解決方案(這是一個比較麻煩的問題,因為其不僅需要重新配置資料庫環境,也對MySQL、PyMySQL的版本有要求,還會受到其他庫的影響從而使配置無法生效)。

下面展示mysql.py的代碼。首先,構造一個名為CreateMySQLTable的類來執行創建表的sql語句。然後,通過mian( )函數來依次執行存放在sql.py中的語句。

這裡請記得提前把連接資料庫的信息存放在settings中(之後會介紹settings中的其他配置問題),如下所示。

Item Pipeline設計

Items代碼模板用於定義所需獲取的數據欄位;而Pipelines是Scrapy處理數據的模塊。

Items

基於前文的7個表,在Items中分別定義7個類,其中,通過語句name = scrapy.Field()定義數據欄位。下面將展示items.py的部分代碼。

以用於保存「主題信息」的DSTopicItem類為例,其主要分為兩部分:一是數據欄位的定義;二是增加一個名為get_insert_sql()的函數。前一部分容易理解,後一部分則是為了簡化Pipelines對不同Item的處理。該函數返回兩個結果,一是insert_sql,即向MySQL中插入數據的語句,二是params,即具體插入的值。其他類的編寫與DSTopicItem相似。

Pipelines

這裡僅構造一個Pipeline來處理Items中的不同情況。先展示代碼,再解釋含義。

這部分代碼較為通用。首先,在存儲數據時用到了Twisted提供的adbapi這一非同步框架,其優勢在於極大地提高了數據寫入資料庫的效率,但在存儲小規模數據時是不必要的。而此處討論的重點則是函數do_insert()的功能:我們將不同Item中get_insert_sql()返回的結果(sql插入語句和vaule值)傳遞給該函數來execute(執行),這樣便達到通過一段代碼處理所有item的目的,即前文所說的「簡化Pipelines對不同Item的處理」。

為使自己定義的MysqlTwistedPipeline生效,需要取消settings中ITEM_PIPELINES的注釋,並進行相應地配置。

Spiders設計

Spiders即爬蟲本身。該部分僅展示主要代碼,不對Scrapy的工作邏輯、網頁解析內容、數據處理細節進行過多說明。

首先,要明確的是「主題—問題—回答—評論」的生成主體是用戶,而同一用戶可以多次生成以上內容。因此,在設計爬蟲時沒有必要在獲取每一個主題、問題、回答或評論時都獲取一次「用戶信息」,因為這樣會造成不必要的開銷,抓取大量重複數據。

考慮到不重複抓取用戶數據這一問題,本文先構造一個名為DS_Zhihu的Spider來解析「主題信息」、「主題下所有問題」、「問題信息」、「問題下所有回答」、「問題的評論」和「回答的評論」。在此基礎上,另構造一個名為DSUserSpider的Spider(在terminal鍵入scrapy genspider DSUserSpider zhihu.com)將上述數據表中的用戶標識抽取出來,去重後再請求「用戶信息」頁面。

考慮到該部分代碼過多,故不完全展示DS_Zhihu和DSUserSpider的所有代碼。

DS_Zhihu

DsZhihuSpider繼承了scrapy.Spider,name為爬蟲名,allowed_domins為所爬網站的域名。其以topic_id(話題id)為入口爬取數據,並依次定義了「主題信息」、「主題下所有問題」、「問題信息」、「問題下所有回答」、「問題的評論」和「回答的評論」的URL。這裡需要注意的是定義請求頭headers,以對抗反爬蟲。

headers請儘可能多地添加信息以模仿瀏覽器,這裡一定請添加的是User-Agent(用戶代理)和Authorization(認證信息),具體請從瀏覽器的「開發者工具」中獲取。在Scrapy中,設置headers中信息的方式很多,這裡沒有選擇在settings中統一進行配置主要是考慮到:一個Project中可能有多個Spider,而每個Spider的headers可能不同。

DsZhihuSpider中定義了7個函數:

start_requests(): 構造起始URL;

get_topic_info(): 獲取topic的基本信息;

get_topic_question(): 獲取主題下的所有question信息;

get_question_info(): 獲取question的基本信息;

get_answer_info(): 獲取question下的所有answer信息;

get_ans_comment(): 獲取所有answer的comment信息;

get_ques_comment(): 獲取每個question的comment信息。

下面將儘可能多地展示每個函數的代碼。

DSUserSpider

該爬蟲主要做兩個工作。

一是去重。定義一個名為get_sql_result()的函數以從MySQL中獲取結果;從「主題信息」、「主題下所有問題」、「問題信息」、「問題下所有回答」、「問題的評論」和「回答的評論」中抽取涉及到「用戶信息」的內容,通過如下的.append、set()、.remove()等操作將重複的用戶唯一標識符(author_url_token)和匿名用戶(空值)從列表中移除。

二是解析start_requests()用於構造起始URL,parse()用於解析網頁內容。主要代碼如下所示。

反反爬蟲及Middlewares設計

到現在為止,「知乎」爬蟲的主要內容已經編寫完成,但由於「知乎」的反爬蟲策略,其並不能良好地運行。因此,該部分將重點討論如何與之對抗。

代理IP

如前文所述,我們通過添加User-Agent和Authorization信息以達到成功請求網頁的目的,但對方還會通過檢測IP訪問流量來判斷請求是否由爬蟲發起。因此,本文將介紹一種方便、經濟的方式來獲取足夠的IP。京東萬象為我們提供了很好的介面(https://wx.jcloud.com/gwtest/init/11088),每個用戶可以免費測試1000次。調用方法可參見該網址,我們可以直接下載Python SDK,並將其(wx_sdk)添加到爬蟲工程中。

此時,我們在DS_Scrapy中新建一個名為IP_Proxy.py的文件,用於獲取、檢測代理IP。先展示代碼,再具體介紹。

這裡定義了6個函數:

get_response(): 從目標網站獲取代理IP。

insert_data(): 執行SQL語句。

save_ip(): 將代理IP保存到資料庫中。

judge_ip(): 檢測代理IP是否可用。

delete_ip(): 從資料庫中將無效IP刪去。

get_all_ip(): 獲取資料庫中的所有IP。

此處可以將get_all_ip()賦給IPPOOL以便在Scrapy中調用。

有幾個問題需要注意:

(1)一定要注意所獲取的代理IP的類型,如HTTPS的網址只能被HTTPS類型的IP所訪問;

(2)檢測IP有效性時建議訪問目標網站而不是https://www.baidu.com等通用網站。

Middlewares設計

Middlewares包括SpiderMiddlewareDownloaderMiddleware兩大部分。

一般地,在需要切換User-Agent和IP時我們會重寫Scrapy的Middleware。下面將展示兩個Middleware:RandomUserAgentMiddleware通過調用fake_useragent來隨機切換User-Agent,但在爬取「知乎」時不建議使用,因為會有很大概率出現由於「瀏覽器版本過低」而無法訪問網頁的問題;RandomProxyMiddleware通過前一部分討論的IPPOOL來實現隨機切換IP的功能,以防止同一IP訪問網站過於頻繁而被封禁的可能。

在設計好RandomProxyMiddleware後,請在settings中進行地相應配置,如下圖所示。

settings配置及其他問題

在之前討論的問題中,已在settings中添加了資料庫信息,也配置了DOWNLOADER_MIDDLEWARES和ITEM_PIPELINES。但還有一些地方需要配置,如ROBOTSTXT_OBEY = False(不遵守robots協議)和COOKIES_ENABLED = False(禁用cookies)等。一般地,我們會根據需求動態地去調整settings中的很多地方,這裡不再展開討論。

至此,DS_Scrapy算是編寫完了,但其實還有一些各種各樣的問題讓我們不得不繼續去優化這個爬蟲。比如,在爬取數據的過程中,有一定概率會出現的307、403、504等無法獲取網頁信息的問題,這時可能需要花費很大的精力去不斷調試、或尋找新的方法去處理此類問題了。

此處討論處理failed_urls的一種方案,借鑒Stack Overflow上關於How to get the scrapy failure URLs(https://stackoverflow.com/questions/13724730/how-to-get-the-scrapy-failure-urls)問題的回答,我們將the scrapy failure urls列印出來,以方便進行相應地處理。當然,將獲取失敗的網址寫入資料庫可能會更好。而如何處理這些無法通過當前這個Scrapy獲取的網址呢,或許這個問題已經變得非常具體了。

下面將展示基於上述方案對DSUserSpider進行改寫後的部分代碼。

運行爬蟲及結果展示

一般通過命令scrapy crawl 來執行爬蟲,如在終端DS_Scrapy這一路徑下鍵入scrapy crawl DS_Zhihuscrapy crawl DSUserSpider來運行爬蟲。然而,為便於調試,我們可以在DS_Scrapy下創建一個名為main.py的文件,通過import scrapy.cmdline中的execute在IDE中運行爬蟲,如下圖所示。

下面,將展示通過DS_Scrapy所獲取的部分數據。

by. AlexChen


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 數據小白工作間 的精彩文章:

TAG:數據小白工作間 |